pax_global_header00006660000000000000000000000064135273132710014516gustar00rootroot0000000000000052 comment=5e3b3c287c0661d27400617f329bd13b3759eb17 sysdig-0.26.4/000077500000000000000000000000001352731327100131115ustar00rootroot00000000000000sysdig-0.26.4/.gitignore000066400000000000000000000003421352731327100151000ustar00rootroot00000000000000*.o *.ko *.ko.unsigned *.cmd *.symvers *.order *.mod *.mod.c *.o.d *.o.rc *.o.ur-safe build/ driver/Makefile driver/driver_config.h *.pyc *.config *.creator *.creator.user* *.files *.includes *.pro.user *.ll .vscode .cache.mk sysdig-0.26.4/.travis-scripts/000077500000000000000000000000001352731327100161645ustar00rootroot00000000000000sysdig-0.26.4/.travis-scripts/linux/000077500000000000000000000000001352731327100173235ustar00rootroot00000000000000sysdig-0.26.4/.travis-scripts/linux/before_install.sh000077500000000000000000000013211352731327100226470ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test sudo apt-get update sysdig-0.26.4/.travis-scripts/linux/build.sh000077500000000000000000000020641352731327100207630ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # set -e export CC="gcc-4.8" export CXX="g++-4.8" wget https://s3.amazonaws.com/download.draios.com/dependencies/cmake-3.3.2.tar.gz tar -xzf cmake-3.3.2.tar.gz cd cmake-3.3.2 ./bootstrap --prefix=/usr make sudo make install cd .. mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE make VERBOSE=1 make package cd .. test/sysdig_trace_regression.sh build/userspace/sysdig/sysdig build/userspace/sysdig/chisels $TRAVIS_BRANCH sysdig-0.26.4/.travis-scripts/linux/install.sh000077500000000000000000000014051352731327100213300ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # sudo apt-get --force-yes install g++-4.8 sudo apt-get install rpm linux-headers-$(uname -r) libelf-dev sudo apt-get purge cmake sysdig-0.26.4/.travis-scripts/osx/000077500000000000000000000000001352731327100167755ustar00rootroot00000000000000sysdig-0.26.4/.travis-scripts/osx/before_install.sh000077500000000000000000000012211352731327100223200ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # brew update sysdig-0.26.4/.travis-scripts/osx/build.sh000077500000000000000000000015431352731327100204360ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # set -e mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DUSE_BUNDLED_LUAJIT=OFF -DUSE_BUNDLED_ZLIB=OFF make install ../test/sysdig_trace_regression.sh $(which sysdig) ./userspace/sysdig/chisels $TRAVIS_BRANCH sysdig-0.26.4/.travis-scripts/osx/install.sh000077500000000000000000000016051352731327100210040ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # function install_if_not_present(){ brew ls | grep ${1} if [[ ${?} -ne 0 ]]; then brew install ${1} else echo "dependency ${1} already installed" fi } install_if_not_present "cmake" install_if_not_present "luajit" install_if_not_present "coreutils" sysdig-0.26.4/.travis.yml000066400000000000000000000022371352731327100152260ustar00rootroot00000000000000# # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # language: c os: - linux - osx env: - BUILD_TYPE=Debug - BUILD_TYPE=Release sudo: required services: - docker before_install: - .travis-scripts/${TRAVIS_OS_NAME}/before_install.sh install: - .travis-scripts/${TRAVIS_OS_NAME}/install.sh before_script: - export KERNELDIR=/lib/modules/$(uname -r)/build script: - .travis-scripts/${TRAVIS_OS_NAME}/build.sh notifications: webhooks: urls: - https://webhooks.gitter.im/e/fdbc2356fb0ea2f15033 on_success: change on_failure: always on_start: never sysdig-0.26.4/CMakeCPackOptions.cmake000066400000000000000000000013361352731327100173540ustar00rootroot00000000000000# # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # if(CPACK_GENERATOR MATCHES "TGZ") set(CPACK_SET_DESTDIR "ON") set(CPACK_STRIP_FILES "OFF") endif() sysdig-0.26.4/CMakeLists.txt000066400000000000000000000502471352731327100156610ustar00rootroot00000000000000# # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # # Prior to doing anything, we make sure that we aren't trying to # run cmake in-tree. (see Issue 71: https://github.com/draios/sysdig/issues/71) if(EXISTS CMakeLists.txt) message(FATAL_ERROR "Looks like you are trying to run cmake from the base sysdig source directory.\n" "** RUNNING CMAKE FROM THE BASE SYSDIG DIRECTORY WILL NOT WORK **\n" "To Fix:\n" " 1. Remove the CMakeCache.txt file in this directory. ex: rm CMakeCache.txt\n" " 2. Create a build directory from here. ex: mkdir build\n" " 3. cd into that directory. ex: cd build\n" " 4. Run cmake from the build directory. ex: cmake ..\n" " 5. Run make from the build directory. ex: make\n" "Full paste-able example:\n" "( rm -f CMakeCache.txt; mkdir build; cd build; cmake ..; make )\n" "The following wiki page has more information on manually building sysdig: http://bit.ly/1oJ84UI") endif() cmake_minimum_required(VERSION 2.8.2) project(sysdig) # Add path for custom CMake modules. list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") if(NOT DEFINED SYSDIG_VERSION) set(SYSDIG_VERSION "0.1.1dev") endif() if(NOT DEFINED DIR_ETC) set(DIR_ETC "${CMAKE_INSTALL_PREFIX}/etc") endif() if(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE Release) endif() set(PACKAGE_NAME "sysdig") add_definitions(-DPLATFORM_NAME="${CMAKE_SYSTEM_NAME}") add_definitions(-DK8S_DISABLE_THREAD) option(BUILD_WARNINGS_AS_ERRORS "Enable building with -Wextra -Werror flags") if(NOT WIN32) set(SYSDIG_DEBUG_FLAGS "-D_DEBUG") set(CMAKE_COMMON_FLAGS "-Wall -ggdb") if(BUILD_WARNINGS_AS_ERRORS) set(CMAKE_SUPPRESSED_WARNINGS "-Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-type-limits -Wno-implicit-fallthrough -Wno-format-truncation") set(CMAKE_COMMON_FLAGS "${CMAKE_COMMON_FLAGS} -Wextra -Werror ${CMAKE_SUPPRESSED_WARNINGS}") endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_COMMON_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_COMMON_FLAGS} -std=c++0x") set(CMAKE_C_FLAGS_DEBUG "${SYSDIG_DEBUG_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${SYSDIG_DEBUG_FLAGS}") set(CMAKE_C_FLAGS_RELEASE "-O3 -fno-strict-aliasing -DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -fno-strict-aliasing -DNDEBUG") if(CMAKE_SYSTEM_NAME MATCHES "Linux") if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(KBUILD_FLAGS "${SYSDIG_DEBUG_FLAGS} ${SYSDIG_FEATURE_FLAGS}") else() set(KBUILD_FLAGS "${SYSDIG_FEATURE_FLAGS}") endif() if(NOT DEFINED PROBE_VERSION) set(PROBE_VERSION "${SYSDIG_VERSION}") endif() if(NOT DEFINED PROBE_NAME) set(PROBE_NAME "sysdig-probe") endif() if(NOT DEFINED PROBE_DEVICE_NAME) set(PROBE_DEVICE_NAME "sysdig") endif() add_subdirectory(driver) add_definitions(-DHAS_CAPTURE) endif() add_subdirectory(scripts) if(CMAKE_SYSTEM_NAME MATCHES "SunOS") set(CMD_MAKE gmake) else() set(CMD_MAKE make) endif() else() set(SYSDIG_FLAGS_WIN "-D_CRT_SECURE_NO_WARNINGS -DWIN32 /EHsc /W3 /Zi") set(SYSDIG_FLAGS_WIN_DEBUG "/MTd /Od") set(SYSDIG_FLAGS_WIN_RELEASE "/MT") set(CMAKE_C_FLAGS "${SYSDIG_FLAGS_WIN}") set(CMAKE_CXX_FLAGS "${SYSDIG_FLAGS_WIN}") set(CMAKE_C_FLAGS_DEBUG "${SYSDIG_FLAGS_WIN_DEBUG}") set(CMAKE_CXX_FLAGS_DEBUG "${SYSDIG_FLAGS_WIN_DEBUG}") set(CMAKE_C_FLAGS_RELEASE "${SYSDIG_FLAGS_WIN_RELEASE}") set(CMAKE_CXX_FLAGS_RELEASE "${SYSDIG_FLAGS_WIN_RELEASE}") endif() if(APPLE) set(CMAKE_EXE_LINKER_FLAGS "-pagezero_size 10000 -image_base 100000000") endif() include(ExternalProject) option(USE_BUNDLED_DEPS "Enable bundled dependencies instead of using the system ones" ON) # # LuaJIT # option(USE_BUNDLED_LUAJIT "Enable building of the bundled LuaJIT" ${USE_BUNDLED_DEPS}) if(NOT USE_BUNDLED_LUAJIT) find_path(LUAJIT_INCLUDE luajit.h PATH_SUFFIXES luajit-2.0 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(Lua51) set(LUAJIT_LIB ${LUA_LIBRARY}) set(LUAJIT_INCLUDE ${LUA_INCLUDE_DIR}) if(NOT ${LUA51_FOUND}) message(FATAL_ERROR "Couldn't find system LuaJIT or Lua") endif() endif() else() set(LUAJIT_SRC "${PROJECT_BINARY_DIR}/luajit-prefix/src/luajit/src") message(STATUS "Using bundled LuaJIT in '${LUAJIT_SRC}'") set(LUAJIT_INCLUDE "${LUAJIT_SRC}") if(NOT WIN32) set(LUAJIT_LIB "${LUAJIT_SRC}/libluajit.a") ExternalProject_Add(luajit URL "http://download.draios.com/dependencies/LuaJIT-2.0.3.tar.gz" URL_MD5 "f14e9104be513913810cd59c8c658dc0" CONFIGURE_COMMAND "" BUILD_COMMAND ${CMD_MAKE} BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${LUAJIT_LIB} INSTALL_COMMAND "") else() set(LUAJIT_LIB "${LUAJIT_SRC}/lua51.lib") ExternalProject_Add(luajit URL "http://download.draios.com/dependencies/LuaJIT-2.0.3.tar.gz" URL_MD5 "f14e9104be513913810cd59c8c658dc0" CONFIGURE_COMMAND "" BUILD_COMMAND msvcbuild.bat BUILD_BYPRODUCTS ${LUAJIT_LIB} BINARY_DIR "${LUAJIT_SRC}" INSTALL_COMMAND "") endif() endif() # # JsonCpp # option(USE_BUNDLED_JSONCPP "Enable building of the bundled jsoncpp" ${USE_BUNDLED_DEPS}) set(JSONCPP_SRC "${PROJECT_SOURCE_DIR}/userspace/libsinsp/third-party/jsoncpp") if(NOT USE_BUNDLED_JSONCPP) find_path(JSONCPP_INCLUDE json/json.h PATH_SUFFIXES jsoncpp) find_library(JSONCPP_LIB NAMES jsoncpp) if(JSONCPP_INCLUDE AND JSONCPP_LIB) message(STATUS "Found jsoncpp: include: ${JSONCPP_INCLUDE}, lib: ${JSONCPP_LIB}") else() message(FATAL_ERROR "Couldn't find system jsoncpp") endif() else() set(JSONCPP_INCLUDE "${JSONCPP_SRC}") set(JSONCPP_LIB_SRC "${JSONCPP_SRC}/jsoncpp.cpp") message(STATUS "Using bundled jsoncpp in '${JSONCPP_SRC}'") endif() # # zlib # option(USE_BUNDLED_ZLIB "Enable building of the bundled zlib" ${USE_BUNDLED_DEPS}) if(NOT USE_BUNDLED_ZLIB) find_path(ZLIB_INCLUDE zlib.h PATH_SUFFIXES zlib) find_library(ZLIB_LIB NAMES z) if(ZLIB_INCLUDE AND ZLIB_LIB) message(STATUS "Found zlib: include: ${ZLIB_INCLUDE}, lib: ${ZLIB_LIB}") else() message(FATAL_ERROR "Couldn't find system zlib") endif() else() set(ZLIB_SRC "${PROJECT_BINARY_DIR}/zlib-prefix/src/zlib") message(STATUS "Using bundled zlib in '${ZLIB_SRC}'") set(ZLIB_INCLUDE "${ZLIB_SRC}") if(NOT WIN32) set(ZLIB_LIB "${ZLIB_SRC}/libz.a") ExternalProject_Add(zlib URL "http://download.draios.com/dependencies/zlib-1.2.11.tar.gz" URL_MD5 "1c9f62f0778697a09d36121ead88e08e" CONFIGURE_COMMAND "./configure" BUILD_COMMAND ${CMD_MAKE} BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${ZLIB_LIB} INSTALL_COMMAND "") else() set(ZLIB_LIB "${ZLIB_SRC}/zdll.lib") ExternalProject_Add(zlib URL "http://download.draios.com/dependencies/zlib-1.2.11.tar.gz" URL_MD5 "1c9f62f0778697a09d36121ead88e08e" CONFIGURE_COMMAND "" BUILD_COMMAND nmake -f win32/Makefile.msc BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${ZLIB_LIB} INSTALL_COMMAND "") endif() endif() # # Intel tbb # if(NOT WIN32) option(USE_BUNDLED_TBB "Enable building of the bundled tbb" ${USE_BUNDLED_DEPS}) if(NOT USE_BUNDLED_TBB) find_path(TBB_INCLUDE_DIR tbb.h PATH_SUFFIXES tbb) find_library(TBB_LIB NAMES tbb) if(TBB_INCLUDE_DIR AND TBB_LIB) message(STATUS "Found tbb: include: ${TBB_INCLUDE_DIR}, lib: ${TBB_LIB}") else() message(FATAL_ERROR "Couldn't find system tbb") endif() else() set(TBB_SRC "${PROJECT_BINARY_DIR}/tbb-prefix/src/tbb") message(STATUS "Using bundled tbb in '${TBB_SRC}'") set(TBB_INCLUDE_DIR "${TBB_SRC}/include/") set(TBB_LIB "${TBB_SRC}/build/lib_release/libtbb.a") ExternalProject_Add(tbb URL "http://s3.amazonaws.com/download.draios.com/dependencies/tbb-2018_U5.tar.gz" URL_MD5 "ff3ae09f8c23892fbc3008c39f78288f" CONFIGURE_COMMAND "" BUILD_COMMAND ${CMD_MAKE} tbb_build_dir=${TBB_SRC}/build tbb_build_prefix=lib extra_inc=big_iron.inc BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${TBB_LIB} INSTALL_COMMAND "") endif() endif() # # jq # if(NOT WIN32 AND NOT APPLE) option(USE_BUNDLED_JQ "Enable building of the bundled jq" ${USE_BUNDLED_DEPS}) if(NOT USE_BUNDLED_JQ) find_path(JQ_INCLUDE jq.h PATH_SUFFIXES jq) find_library(JQ_LIB NAMES jq) if(JQ_INCLUDE AND JQ_LIB) message(STATUS "Found jq: include: ${JQ_INCLUDE}, lib: ${JQ_LIB}") else() message(FATAL_ERROR "Couldn't find system jq") endif() else() set(JQ_SRC "${PROJECT_BINARY_DIR}/jq-prefix/src/jq") message(STATUS "Using bundled jq in '${JQ_SRC}'") set(JQ_INCLUDE "${JQ_SRC}") set(JQ_LIB "${JQ_SRC}/.libs/libjq.a") ExternalProject_Add(jq URL "http://download.draios.com/dependencies/jq-1.5.tar.gz" URL_MD5 "0933532b086bd8b6a41c1b162b1731f9" CONFIGURE_COMMAND ./configure --disable-maintainer-mode --enable-all-static --disable-dependency-tracking BUILD_COMMAND ${CMD_MAKE} LDFLAGS=-all-static BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${JQ_LIB} PATCH_COMMAND wget -O jq-1.5-fix-tokenadd.patch https://github.com/stedolan/jq/commit/8eb1367ca44e772963e704a700ef72ae2e12babd.patch && patch -i jq-1.5-fix-tokenadd.patch INSTALL_COMMAND "") endif() endif() # # ncurses, keep it simple for the moment # if(NOT WIN32) option(USE_BUNDLED_NCURSES "Enable building of the bundled ncurses" ${USE_BUNDLED_DEPS}) if(NOT USE_BUNDLED_NCURSES) set(CURSES_NEED_NCURSES TRUE) find_package(Curses REQUIRED) message(STATUS "Found ncurses: include: ${CURSES_INCLUDE_DIR}, lib: ${CURSES_LIBRARIES}") else() set(CURSES_BUNDLE_DIR "${PROJECT_BINARY_DIR}/ncurses-prefix/src/ncurses") set(CURSES_INCLUDE_DIR "${CURSES_BUNDLE_DIR}/include/") set(CURSES_LIBRARIES "${CURSES_BUNDLE_DIR}/lib/libncurses.a") message(STATUS "Using bundled ncurses in '${CURSES_BUNDLE_DIR}'") ExternalProject_Add(ncurses URL "http://download.draios.com/dependencies/ncurses-6.0-20150725.tgz" URL_MD5 "32b8913312e738d707ae68da439ca1f4" CONFIGURE_COMMAND ./configure --without-cxx --without-cxx-binding --without-ada --without-manpages --without-progs --without-tests --with-terminfo-dirs=/etc/terminfo:/lib/terminfo:/usr/share/terminfo BUILD_COMMAND ${CMD_MAKE} BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${CURSES_LIBRARIES} INSTALL_COMMAND "") endif() endif() if(NOT WIN32 AND NOT APPLE) # # libb64 # option(USE_BUNDLED_B64 "Enable building of the bundled b64" ${USE_BUNDLED_DEPS}) if(NOT USE_BUNDLED_B64) find_path(B64_INCLUDE NAMES b64/encode.h) find_library(B64_LIB NAMES b64) if(B64_INCLUDE AND B64_LIB) message(STATUS "Found b64: include: ${B64_INCLUDE}, lib: ${B64_LIB}") else() message(FATAL_ERROR "Couldn't find system b64") endif() else() set(B64_SRC "${PROJECT_BINARY_DIR}/b64-prefix/src/b64") message(STATUS "Using bundled b64 in '${B64_SRC}'") set(B64_INCLUDE "${B64_SRC}/include") set(B64_LIB "${B64_SRC}/src/libb64.a") ExternalProject_Add(b64 URL "http://download.draios.com/dependencies/libb64-1.2.src.zip" URL_MD5 "a609809408327117e2c643bed91b76c5" CONFIGURE_COMMAND "" BUILD_COMMAND ${CMD_MAKE} BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${B64_LIB} INSTALL_COMMAND "") endif() endif() if(NOT WIN32) # # OpenSSL # option(USE_BUNDLED_OPENSSL "Enable building of the bundled OpenSSL" ${USE_BUNDLED_DEPS}) if(NOT USE_BUNDLED_OPENSSL) find_package(OpenSSL REQUIRED) message(STATUS "Found OpenSSL: include: ${OPENSSL_INCLUDE_DIR}, lib: ${OPENSSL_LIBRARIES}") else() set(OPENSSL_BUNDLE_DIR "${PROJECT_BINARY_DIR}/openssl-prefix/src/openssl") set(OPENSSL_INSTALL_DIR "${OPENSSL_BUNDLE_DIR}/target") set(OPENSSL_INCLUDE_DIR "${PROJECT_BINARY_DIR}/openssl-prefix/src/openssl/include") set(OPENSSL_LIBRARY_SSL "${OPENSSL_INSTALL_DIR}/lib/libssl.a") set(OPENSSL_LIBRARY_CRYPTO "${OPENSSL_INSTALL_DIR}/lib/libcrypto.a") message(STATUS "Using bundled openssl in '${OPENSSL_BUNDLE_DIR}'") ExternalProject_Add(openssl URL "http://download.draios.com/dependencies/openssl-1.0.2n.tar.gz" URL_MD5 "13bdc1b1d1ff39b6fd42a255e74676a4" CONFIGURE_COMMAND ./config shared --prefix=${OPENSSL_INSTALL_DIR} BUILD_COMMAND ${CMD_MAKE} BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${OPENSSL_LIBRARY_SSL} ${OPENSSL_LIBRARY_CRYPTO} INSTALL_COMMAND ${CMD_MAKE} install) endif() # # libcurl # option(USE_BUNDLED_CURL "Enable building of the bundled curl" ${USE_BUNDLED_DEPS}) if(NOT USE_BUNDLED_CURL) find_package(CURL REQUIRED) message(STATUS "Found CURL: include: ${CURL_INCLUDE_DIR}, lib: ${CURL_LIBRARIES}") else() set(CURL_BUNDLE_DIR "${PROJECT_BINARY_DIR}/curl-prefix/src/curl") set(CURL_INCLUDE_DIR "${CURL_BUNDLE_DIR}/include/") set(CURL_LIBRARIES "${CURL_BUNDLE_DIR}/lib/.libs/libcurl.a") if(NOT USE_BUNDLED_OPENSSL) set(CURL_SSL_OPTION "--with-ssl") else() set(CURL_SSL_OPTION "--with-ssl=${OPENSSL_INSTALL_DIR}") message(STATUS "Using bundled curl in '${CURL_BUNDLE_DIR}'") message(STATUS "Using SSL for curl in '${CURL_SSL_OPTION}'") endif() ExternalProject_Add(curl DEPENDS openssl URL "http://download.draios.com/dependencies/curl-7.61.0.tar.bz2" URL_MD5 "31d0a9f48dc796a7db351898a1e5058a" CONFIGURE_COMMAND ./configure ${CURL_SSL_OPTION} --disable-threaded-resolver --disable-shared --enable-optimize --disable-curldebug --disable-rt --enable-http --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-sspi --disable-ntlm-wb --disable-tls-srp --without-winssl --without-darwinssl --without-polarssl --without-cyassl --without-nss --without-axtls --without-ca-path --without-ca-bundle --without-libmetalink --without-librtmp --without-winidn --without-libidn --without-libidn2 --without-nghttp2 --without-libssh2 --without-libpsl BUILD_COMMAND ${CMD_MAKE} BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${CURL_LIBRARIES} INSTALL_COMMAND "") endif() endif() # NOT WIN32 AND NOT APPLE if(NOT WIN32 AND NOT APPLE) option(USE_BUNDLED_CARES "Enable building of the bundled c-ares" ${USE_BUNDLED_DEPS}) if(NOT USE_BUNDLED_CARES) find_path(CARES_INCLUDE NAMES cares/ares.h ares.h) find_library(CARES_LIB NAMES cares) if(CARES_INCLUDE AND CARES_LIB) message(STATUS "Found c-ares: include: ${CARES_INCLUDE}, lib: ${CARES_LIB}") else() message(FATAL_ERROR "Couldn't find system c-ares") endif() else() set(CARES_SRC "${PROJECT_BINARY_DIR}/c-ares-prefix/src/c-ares") message(STATUS "Using bundled c-ares in '${CARES_SRC}'") set(CARES_INCLUDE "${CARES_SRC}/target/include") set(CARES_LIB "${CARES_SRC}/target/lib/libcares.a") ExternalProject_Add(c-ares URL "http://download.sysdig.com/dependencies/c-ares-1.13.0.tar.gz" URL_MD5 "d2e010b43537794d8bedfb562ae6bba2" CONFIGURE_COMMAND ./configure --prefix=${CARES_SRC}/target BUILD_COMMAND ${CMD_MAKE} BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${CARES_INCLUDE} ${CARES_LIB} INSTALL_COMMAND ${CMD_MAKE} install) endif() option(USE_BUNDLED_PROTOBUF "Enable building of the bundled protobuf" ${USE_BUNDLED_DEPS}) if(NOT USE_BUNDLED_PROTOBUF) find_program(PROTOC NAMES protoc) find_path(PROTOBUF_INCLUDE NAMES google/protobuf/message.h) find_library(PROTOBUF_LIB NAMES protobuf) if(PROTOC AND PROTOBUF_INCLUDE AND PROTOBUF_LIB) message(STATUS "Found protobuf: compiler: ${PROTOC}, include: ${PROTOBUF_INCLUDE}, lib: ${PROTOBUF_LIB}") else() message(FATAL_ERROR "Couldn't find system protobuf") endif() else() set(PROTOBUF_SRC "${PROJECT_BINARY_DIR}/protobuf-prefix/src/protobuf") message(STATUS "Using bundled protobuf in '${PROTOBUF_SRC}'") set(PROTOC "${PROTOBUF_SRC}/target/bin/protoc") set(PROTOBUF_INCLUDE "${PROTOBUF_SRC}/target/include") set(PROTOBUF_LIB "${PROTOBUF_SRC}/target/lib/libprotobuf.a") ExternalProject_Add(protobuf DEPENDS openssl zlib URL "http://download.sysdig.com/dependencies/protobuf-cpp-3.5.0.tar.gz" URL_MD5 "e4ba8284a407712168593e79e6555eb2" # TODO what if using system zlib? CONFIGURE_COMMAND /usr/bin/env CPPFLAGS=-I${ZLIB_INCLUDE} LDFLAGS=-L${ZLIB_SRC} ./configure --with-zlib --prefix=${PROTOBUF_SRC}/target BUILD_COMMAND ${CMD_MAKE} BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${PROTOC} ${PROTOBUF_INCLUDE} ${PROTOBUF_LIB} # TODO s390x support INSTALL_COMMAND make install) endif() option(USE_BUNDLED_GRPC "Enable building of the bundled grpc" ${USE_BUNDLED_DEPS}) if(NOT USE_BUNDLED_GRPC) find_path(GRPCXX_INCLUDE NAMES grpc++/grpc++.h) if(GRPCXX_INCLUDE) set(GRPC_INCLUDE ${GRPCXX_INCLUDE}) else() find_path(GRPCPP_INCLUDE NAMES grpcpp/grpcpp.h) set(GRPC_INCLUDE ${GRPCPP_INCLUDE}) add_definitions(-DGRPC_INCLUDE_IS_GRPCPP=1) endif() find_library(GRPC_LIB NAMES grpc_unsecure) find_library(GRPCPP_LIB NAMES grpc++_unsecure) if(GRPC_INCLUDE AND GRPC_LIB AND GRPCPP_LIB) message(STATUS "Found grpc: include: ${GRPC_INCLUDE}, C lib: ${GRPC_LIB}, C++ lib: ${GRPCPP_LIB}") else() message(FATAL_ERROR "Couldn't find system grpc") endif() find_program(GRPC_CPP_PLUGIN grpc_cpp_plugin) if(NOT GRPC_CPP_PLUGIN) message(FATAL_ERROR "System grpc_cpp_plugin not found") endif() else() find_package(PkgConfig) if(NOT PKG_CONFIG_FOUND) message(FATAL_ERROR "pkg-config binary not found") endif() message(STATUS "Found pkg-config executable: ${PKG_CONFIG_EXECUTABLE}") set(GRPC_SRC "${PROJECT_BINARY_DIR}/grpc-prefix/src/grpc") message(STATUS "Using bundled grpc in '${GRPC_SRC}'") set(GRPC_INCLUDE "${GRPC_SRC}/include") set(GRPC_LIB "${GRPC_SRC}/libs/opt/libgrpc_unsecure.a") set(GRPCPP_LIB "${GRPC_SRC}/libs/opt/libgrpc++_unsecure.a") set(GRPC_CPP_PLUGIN "${GRPC_SRC}/bins/opt/grpc_cpp_plugin") get_filename_component(PROTOC_DIR ${PROTOC} PATH) ExternalProject_Add(grpc DEPENDS protobuf zlib c-ares URL "http://download.draios.com/dependencies/grpc-1.8.1.tar.gz" URL_MD5 "2fc42c182a0ed1b48ad77397f76bb3bc" CONFIGURE_COMMAND "" # TODO what if using system openssl, protobuf or cares? BUILD_COMMAND CFLAGS=-Wno-implicit-fallthrough HAS_SYSTEM_ZLIB=false LDFLAGS=-static PATH=${PROTOC_DIR}:$ENV{PATH} PKG_CONFIG_PATH=${OPENSSL_BUNDLE_DIR}:${PROTOBUF_SRC}:${CARES_SRC} PKG_CONFIG=${PKG_CONFIG_EXECUTABLE} make grpc_cpp_plugin static_cxx static_c BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${GRPC_LIB} ${GRPCPP_LIB} # TODO s390x support # TODO what if using system zlib PATCH_COMMAND rm -rf third_party/zlib && ln -s ${ZLIB_SRC} third_party/zlib && wget https://download.sysdig.com/dependencies/grpc-1.8.1-Makefile.patch && patch < grpc-1.8.1-Makefile.patch INSTALL_COMMAND "") endif() endif() add_subdirectory(userspace/sysdig) add_subdirectory(userspace/libscap) add_subdirectory(userspace/libsinsp) #add_subdirectory(userspace/falcobl) set(CPACK_PACKAGE_NAME "${PACKAGE_NAME}") set(CPACK_PACKAGE_VENDOR "Sysdig Inc.") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "sysdig, a system-level exploration and troubleshooting tool") set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/scripts/description.txt") set(CPACK_PACKAGE_VERSION "${SYSDIG_VERSION}") set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_PROCESSOR}") set(CPACK_PROJECT_CONFIG_FILE "${PROJECT_SOURCE_DIR}/CMakeCPackOptions.cmake") set(CPACK_STRIP_FILES "ON") set(CPACK_GENERATOR DEB RPM TGZ) set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Sysdig ") set(CPACK_DEBIAN_PACKAGE_SECTION "utils") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://www.sysdig.org") set(CPACK_DEBIAN_PACKAGE_DEPENDS "dkms (>= 2.1.0.0)") set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/scripts/debian/postinst;${CMAKE_BINARY_DIR}/scripts/debian/prerm") set(CPACK_RPM_PACKAGE_LICENSE "Apache v2.0") set(CPACK_RPM_PACKAGE_URL "http://www.sysdig.org") set(CPACK_RPM_PACKAGE_REQUIRES "dkms, gcc, make, kernel-devel, perl") set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/postinstall") set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/preuninstall") set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/src /usr/share/man /usr/share/man/man8) include(CPack) sysdig-0.26.4/COPYING000066400000000000000000000262641352731327100141560ustar00rootroot00000000000000The contents of the driver/ subdirectory are licensed separately--see COPYING.driver. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. sysdig-0.26.4/NOTICES000066400000000000000000000024121352731327100141370ustar00rootroot00000000000000Copyright (C) 2013-2018 Draios Inc. dba Sysdig 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. SYSDIG SUBCOMPONENTS: -The files in and its subdirectories are used to compile the kernel module and may be injected into eBPF; these are dual licensed under the MIT license or the GNU General Public License 2. Copies of both licenses are available in the subdirectory. -The following files are under Apache 2.0: userspace/sysdig/chisels/fileslower.lua, Copyright (C) 2014 Brendan Gregg userspace/sysdig/chisels/memcachelog.lua, Copyright (C) 2015 Donatas Abraitis userspace/sysdig/chisels/subsecoffset.lua, Copryight (C) 2013-2014 Draios Inc. dba Sysdig, Copyright (C) 2015 Brendan Gregg userspace/sysdig/chisels/v_backlog.lua, Copyright (C) Donatas Abraitis sysdig-0.26.4/README.md000066400000000000000000000163671352731327100144050ustar00rootroot00000000000000sysdig ====== [![Build Status](https://travis-ci.org/draios/sysdig.png?branch=master)](https://travis-ci.org/draios/sysdig) # Welcome to **sysdig**! **Sysdig** is a universal system visibility tool with native support for containers: `~$ sysdig` **Csysdig** is a simple, intuitive, and fully customizable curses UI for sysdig: `~$ csysdig` Where to start? --- If this is your first time hearing about sysdig, we recommend you [start with the website](http://www.sysdig.org). What does sysdig do and why should I use it? --- **Sysdig is a simple tool for deep system visibility, with native support for containers.** We built sysdig to give you _easy access_ to the actual behavior of your Linux systems and containers. Honestly, the best way to understand sysdig is to [try it](https://github.com/draios/sysdig/wiki/How-to-Install-Sysdig-for-Linux) - its super easy! Or here's a quick video introduction to csysdig, the simple, intuitive, and fully customizable curses-based UI for sysdig: https://www.youtube.com/watch?v=UJ4wVrbP-Q8 Far too often, system-level monitoring and troubleshooting still involves logging into a machine with SSH and using a plethora of dated tools with very inconsistent interfaces. And many of these classic Linux tools breakdown completely in containerized environments. Sysdig unites your Linux toolkit into a single, consistent, easy-to-use interface. And sysdig's unique architecture allows deep inspection into containers, right out of the box, without having to instrument the containers themselves in any way. Sysdig instruments your physical and virtual machines at the OS level by installing into the Linux kernel and capturing system calls and other OS events. Sysdig also makes it possible to create trace files for system activity, similarly to what you can do for networks with tools like tcpdump and Wireshark. This way, problems can be analyzed at a later time, without losing important information. Rich system state is stored in the trace files, so that the captured activity can be put into full context. Think about sysdig as strace + tcpdump + htop + iftop + lsof + ...awesome sauce. Documentation / Support --- [Visit the wiki](https://github.com/draios/sysdig/wiki) for full documentation on sysdig and its APIs. For support using sysdig, please contact [the official mailing list](https://groups.google.com/forum/#!forum/sysdig). Join the Community --- * Contact the [official mailing list](https://groups.google.com/forum/#!forum/sysdig) for support and to talk with other users * Follow us on [Twitter](https://twitter.com/sysdig) * This is our [blog](https://sysdig.com/blog/). There are many like it, but this one is ours. * Join our [Public Slack](https://slack.sysdig.com) channel for sysdig announcements and discussions. License Terms --- The sysdig userspace programs and supporting code are licensed to you under the [Apache 2.0](./COPYING) open source license. The sysdig kernel module, which is in the `driver` subdirectory, is licensed to you under both the [MIT](https://github.com/draios/sysdig/blob/dev/driver/MIT.txt) and [GPLv2](https://github.com/draios/sysdig/blob/dev/driver/GPL2.txt) open source licenses. Contributor License Agreements --- ### Background As sysdig matures and gains wider acceptance, we are formalizing the way that we accept contributions of code from the contributing community. We must now ask that contributions to sysdig be provided subject to the terms and conditions of a [Contributor License Agreement (CLA)](https://github.com/draios/sysdig/tree/dev/cla). The CLA comes in two forms, applicable to contributions by individuals, or by legal entities such as corporations and their employees. We recognize that entering into a CLA with us involves real consideration on your part, and we’ve tried to make this process as clear and simple as possible. We’ve modeled our CLA off of industry standards, such as [the CLA used by Kubernetes](https://github.com/kubernetes/kubernetes/blob/master/CONTRIBUTING.md). Note that this agreement is not a transfer of copyright ownership, this simply is a license agreement for contributions, intended to clarify the intellectual property license granted with contributions from any person or entity. It is for your protection as a contributor as well as the protection of sysdig; it does not change your rights to use your own contributions for any other purpose. For some background on why contributor license agreements are necessary, you can read FAQs from many other open source projects: - [Django’s excellent CLA FAQ](https://www.djangoproject.com/foundation/cla/faq/) - [A well-written chapter from Karl Fogel’s Producing Open Source Software on CLAs](http://producingoss.com/en/copyright-assignment.html) - [The Wikipedia article on CLAs](http://en.wikipedia.org/wiki/Contributor_license_agreement) As always, we are grateful for your past and present contributions to sysdig. ### What do I need to do in order to contribute code? **Individual contributions**: Individuals who wish to make contributions must review the [Individual Contributor License Agreement](https://github.com/draios/sysdig/blob/dev/cla/sysdig_contributor_agreement.txt) and indicate agreement by adding the following line to every GIT commit message: ``` sysdig-CLA-1.0-signed-off-by: Joe Smith ``` Use your real name; pseudonyms or anonymous contributions are not allowed. **Corporate contributions**: Employees of corporations, members of LLCs or LLPs, or others acting on behalf of a contributing entity, must review the [Corporate Contributor License Agreement](https://github.com/draios/sysdig/blob/dev/cla/sysdig_corp_contributor_agreement.txt), must be an authorized representative of the contributing entity, and indicate agreement to it on behalf of the contributing entity by adding the following lines to every GIT commit message: ``` sysdig-CLA-1.0-contributing-entity: Full Legal Name of Entity sysdig-CLA-1.0-signed-off-by: Joe Smith ``` Use a real name of a natural person who is an authorized representative of the contributing entity; pseudonyms or anonymous contributions are not allowed. **Government contributions**: Employees or officers of the United States Government, must review the [Corporate Contributor License Agreement](https://github.com/draios/sysdig/blob/dev/cla/sysdig_corp_contributor_agreement.txt), must be an authorized representative of the contributing entity, and indicate agreement to it on behalf of the contributing entity by adding the following lines to every GIT commit message: ``` sysdig-CLA-1.0-contributing-govt-entity: Full Legal Name of Entity sysdig-CLA-1.0-signed-off-by: Joe Smith This file is a work of authorship of an employee or officer of the United States Government and is not subject to copyright in the United States under 17 USC 105. ``` Use a real name of a natural person who is an authorized representative of the contributing entity; pseudonyms or anonymous contributions are not allowed. Sysdig Cloud --- Interested in a fully supported, fully distributed version of sysdig? Check out [Sysdig Monitor](https://sysdig.com/product)! Open source sysdig is proudly supported by [Sysdig Inc](https://sysdig.com/company/). Interested in what we're doing? [Sysdig is hiring](https://sysdig.com/jobs/). sysdig-0.26.4/cla/000077500000000000000000000000001352731327100136505ustar00rootroot00000000000000sysdig-0.26.4/cla/sysdig_contributor_agreement.txt000066400000000000000000000151121352731327100223740ustar00rootroot00000000000000DRAIOS, INC. – OPEN SOURCE CONTRIBUTION LICENSE AGREEMENT (“Agreement”) Draios, Inc. dba Sysdig (“Draios” or “Sysdig”) welcomes you to work on our open source software projects. In order to clarify the intellectual property license granted with Contributions from any person or entity, you must agree to the license terms below in order to contribute code back to our repositories. This license is for your protection as a Contributor as well as the protection of Sysdig; it does not change your rights to use your own Contributions for any other purpose. To indicate your Agreement, follow the procedure set forth below under TO AGREE, after reading this Agreement. You accept and agree to the following terms and conditions for Your present and future Contributions submitted to Draios/Sysdig. Except for the license granted herein to Draios/Sysdig and recipients of software distributed by Draios/Sysdig, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the individual natural person and copyright owner who is making this Agreement with Draios/Sysdig. “You” excludes legal entities such as corporations, and Draios/Sysdig provides a separate CLA for corporations or other entities. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to Draios/Sysdig for inclusion in, or documentation of, any of the products owned or managed by Draios/Sysdig (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to Draios/Sysdig or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Draios/Sysdig for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to Draios/Sysdig and to recipients of software distributed by Draios/Sysdig a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to Draios/Sysdig and to recipients of software distributed by Draios/Sysdig a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims that You have the right to license and that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity other than Draios/Sysdig institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent to Draios/Sysdig that You are legally entitled to grant the licenses set forth above. 5. You represent that each of Your Contributions is Your original creation unless you act according to section 7 below. You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which You are personally aware and which are associated with any part of Your Contributions. You represent that Your sign-off indicating assent to this Agreement includes your real name and not a pseudonym, and that you shall not attempt or make an anonymous Contribution. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions to Draios/Sysdig on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. If You wish to submit work that is not Your original creation, You may submit it to Draios/Sysdig separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify Draios/Sysdig of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. 9. You understand and agree that this project and Your Contribution are public and that a record of the contribution, including all personal information that I submit with it, including my sign-off, may be stored by Draios/Sysdig indefinitely and may be redistributed to others. You understand and agree that Draios/Sysdig has no obligation to use any Contribution in any Draios/Sysdig project or product, and Draios/Sysdig may decline to accept Your Contributions or Draios/Sysdig may remove Your Contributions from Draios/Sysdig projects or products at any time without notice. You understand and agree that Draios/Sysdig is not and will not pay you any form of compensation, in currency, equity or otherwise, in exchange for Your Contributions or for Your assent to this Agreement. You understand and agree that you are independent of Draios/Sysdig and you are not, by entering into this Agreement or providing Your Contributions, becoming employed, hired as an independent contractor, or forming any other relationship with Draios/Sysdig relating to employment, compensation or ownership or involving any fiduciary obligation. TO AGREE: Add the following line to every GIT commit message: sysdig-CLA-1.0-signed-off-by: Joe Smith Use your real name; pseudonyms or anonymous contributions are not allowed.sysdig-0.26.4/cla/sysdig_corp_contributor_agreement.txt000066400000000000000000000171161352731327100234250ustar00rootroot00000000000000DRAIOS, INC. – OPEN SOURCE CONTRIBUTION LICENSE AGREEMENT FOR CONTRIBUTING ENTITIES (SUCH AS CORPORATIONS) (“Agreement”) Draios, Inc. dba Sysdig (“Draios” or “Sysdig”) welcomes you to work on our open source software projects. In order to clarify the intellectual property license granted with Contributions from any person or entity, you must agree to the license terms below in order to contribute code back to our repositories. This license is for your protection as a Contributor as well as the protection of Sysdig; it does not change your rights to use your own Contributions for any other purpose. To indicate your Agreement, follow the procedure set forth below under TO AGREE, after reading this Agreement. A “contributing entity” means a corporation, limited liability company, partnership, or other entity that is organized and recognized under the laws of a state of the United States or another country (a “contributing entity”). We provide a separate CLA for individual contributors. You accept and agree to the following terms and conditions for Your present and future Contributions that are submitted to Draios/Sysdig. Except for the license granted herein to Draios/Sysdig and recipients of software distributed by Draios/Sysdig, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the contributing entity that owns for copyright purposes or otherwise has the right to contribute the Contribution, and that is making this Agreement with Draios/Sysdig, and all other entities that control, are controlled by, or are under common control with the contributing entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to Draios/Sysdig for inclusion in, or documentation of, any of the products owned or managed by Draios/Sysdig (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to Draios/Sysdig or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Draios/Sysdig for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to Draios/Sysdig and to recipients of software distributed by Draios/Sysdig a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to Draios/Sysdig and to recipients of software distributed by Draios/Sysdig a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims that You have the right to license and that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity other than Draios/Sysdig institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent to Draios/Sysdig that You own or have the right to contribute Your Contributions to Draios/Sysdig, and that You are legally entitled to grant the licenses set forth above. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which You are personally aware and which are associated with any part of Your Contributions. You represent that Your sign-off indicating assent to this Agreement includes the real name of a natural person who is an authorized representative of You, and not a pseudonym, and that You are not attempting or making an anonymous Contribution. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions to Draios/Sysdig on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. If You wish to submit work that is not Your original creation, You may submit it to Draios/Sysdig separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which You are aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify Draios/Sysdig of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. 9. You understand and agree that this project and Your Contribution are public and that a record of the contribution, including all personal information that You submit with it, including the sign-off of Your authorized representative, may be stored by Draios/Sysdig indefinitely and may be redistributed to others. You understand and agree that Draios/Sysdig has no obligation to use any Contribution in any Draios/Sysdig project or product, and Draios/Sysdig may decline to accept Your Contributions or Draios/Sysdig may remove Your Contributions from Draios/Sysdig projects or products at any time without notice. You understand and agree that Draios/Sysdig is not and will not pay You any form of compensation, in currency, equity or otherwise, in exchange for Your Contributions or for Your assent to this Agreement. You understand and agree that You are independent of Draios/Sysdig and You are not, by entering into this Agreement or providing Your Contributions, becoming employed, hired as an independent contractor, or forming any other relationship with Draios/Sysdig relating to employment, compensation or ownership or involving any fiduciary obligation. TO AGREE: Add the following lines to every GIT commit message: sysdig-CLA-1.0-contributing-entity: Full Legal Name of Entity sysdig-CLA-1.0-signed-off-by: Joe Smith Use a real name of a natural person who is an authorized representative of the contributing entity; pseudonyms or anonymous contributions are not allowed. sysdig-0.26.4/cla/sysdig_govt_contributor_agreement.txt000066400000000000000000000157231352731327100234430ustar00rootroot00000000000000DRAIOS, INC. OPEN SOURCE CONTRIBUTION AGREEMENT FOR UNITED STATES GOVERNMENT CONTRIBUTING ENTITIES (Agreement) Draios, Inc. (Draios or Sysdig) welcomes the work of others on our open source software projects. To contribute code back to our repositories, we require a contributing entity that is a United States Government agency to complete, and agree to, the Government Contributor Agreement (GCA) set forth here, by and through a designated authorized representative. This agreement clarifies the ability for us to use and incorporate the contributions of a government contributing entity in our projects and products. After agreeing to these terms, a contributing entity may contribute to our projects. To indicate the agreement of the contributing entity, an authorized representative shall follow the procedure set forth below under TO AGREE, after reading this Agreement. A contributing entity means any agency or unit of the United States government. We provide a separate CLA for individual contributors. You accept and agree to the following terms and conditions for Your present and future Contributions that are submitted to Draios/Sysdig. 1. Definitions. "You" (or "Your") shall mean the contributing entity that has authored or otherwise has the right to contribute the Contribution, and that is making this Agreement with Draios/Sysdig. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to Draios/Sysdig for inclusion in, or documentation of, any of the products owned or managed by Draios/Sysdig (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to Draios/Sysdig or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Draios/Sysdig for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Contributions Not Subject to Copyright. Each Contribution is a work authored by the United States Government or an employee or officer thereof and is not subject to copyright under 17 U.S.C. 105. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to Draios/Sysdig and to recipients of software distributed by Draios/Sysdig a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims that You have the right to license and that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity other than Draios/Sysdig institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent to Draios/Sysdig that You own or have the right to contribute Your Contributions to Draios/Sysdig, and that You are legally entitled to grant the license set forth above. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which You are personally aware and which are associated with any part of Your Contributions. You represent that Your sign-off indicating assent to this Agreement includes the real name of a natural person who is an authorized representative of You, and not a pseudonym, and that You are not attempting or making an anonymous Contribution. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions to Draios/Sysdig on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. If You wish to submit work that is not Your original creation, You may submit it to Draios/Sysdig separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which You are aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify Draios/Sysdig of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. 9. You understand and agree that this project and Your Contribution are public and that a record of the contribution, including all personal information that You submit with it, including the sign-off of Your authorized representative, may be stored by Draios/Sysdig indefinitely and may be redistributed to others. You understand and agree that Draios/Sysdig has no obligation to use any Contribution in any Draios/Sysdig project or product, and Draios/Sysdig may decline to accept Your Contributions or Draios/Sysdig may remove Your Contributions from Draios/Sysdig projects or products at any time without notice. You understand and agree that Draios/Sysdig is not and will not pay You any form of compensation, in currency, equity or otherwise, in exchange for Your Contributions or for Your assent to this Agreement. You understand and agree that You are independent of Draios/Sysdig and You are not, by entering into this Agreement or providing Your Contributions, becoming employed, hired as an independent contractor, or forming any other relationship with Draios/Sysdig relating to employment, compensation or ownership or involving any fiduciary obligation. TO AGREE: Add the following lines to every GIT commit message: sysdig-CLA-1.0-contributing-govt-entity: Full Legal Name of Entity sysdig-CLA-1.0-signed-off-by: Joe Smith joe.smith@email.com This file is a work of authorship of an employee or officer of the United States Government and is not subject to copyright in the United States under 17 USC 105. Use a real name of a natural person who is an authorized representative of the contributing entity; pseudonyms or anonymous contributions are not allowed. sysdig-0.26.4/cmake/000077500000000000000000000000001352731327100141715ustar00rootroot00000000000000sysdig-0.26.4/cmake/modules/000077500000000000000000000000001352731327100156415ustar00rootroot00000000000000sysdig-0.26.4/cmake/modules/FindMakedev.cmake000066400000000000000000000024251352731327100210230ustar00rootroot00000000000000# # Copyright (C) 2013-2019 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # # This module is used to understand where the makedev function # is defined in the glibc in use. # see 'man 3 makedev' # Usage: # In your CMakeLists.txt # include(FindMakedev) # # In your source code: # # #if HAVE_SYS_MKDEV_H # #include # #endif # #ifdef HAVE_SYS_SYSMACROS_H # #include # #endif # include(${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake) check_include_file("sys/mkdev.h" HAVE_SYS_MKDEV_H) check_include_file("sys/sysmacros.h" HAVE_SYS_SYSMACROS_H) if (HAVE_SYS_MKDEV_H) add_definitions(-DHAVE_SYS_MKDEV_H) endif() if (HAVE_SYS_SYSMACROS_H) add_definitions(-DHAVE_SYS_SYSMACROS_H) endif() sysdig-0.26.4/coding_conventions.md000066400000000000000000000142621352731327100173300ustar00rootroot000000000000000 Introduction ------ Sysdig strives for a consistent high quality code base and uses the conventions below. If you are going to commit code that doesn't follow them, then you put the work on us. :-(. If you use vim or emacs, you can put a custom configuration file in the base directory of sysdig in order to follow the conventions. Also, note that the conventions in this file apply **strictly to the userspace** part of sysdig. For the kernel code, you should refer to https://www.kernel.org/doc/html/latest/process/coding-style.html and always run checkpatch.pl from the kernel tree before submitting pull requests. Thanks for your attention and time. 1 Curly Braces ------ Every curly brace ("{" and "}") should go on its own line. Example: if(a == 0) { b = 1; } 2 If and for statements ------ Every `if` and `for` statement should have the curly braces. Example: if(a == 0) { b = 1; } and not if(a == 0) b = 1; 3 Whitespace usage ------ Spaces are used in the following way: int32_t foo(int32_t a, int32_t b) { for(j = 0; j < 10; j++) { foo(a, b); } } Note that: * in a function declaration, there is no space between the function name and the "(". * in a function declaration, there is no space between the "(" and the first parameter. * in a statement (e.g `for`, `while`...), there is no space between the "for" and the "(". * in a statement (e.g `for`), there is no space between the "(" and the variable name. * in a function call, there is no space between the function name and the "(". * in a function call, there is no space between the "(" and the first parameter. * "," and ";" work like in English: there should be a space after them. 4 Primitive types ------ For portability reasons, please use the standard C99 types instead of the native C types like `int` and `long`. C99 types types will be available in all the user level sysdig source files: Example: int32_t foo; 5 Commenting Style ------ Comments should be in the C++ style so we can use `/* */` to quickly remove portions of code during development. Example: // this is a comment 6 Commenting Content ------ Code comments work in the following 2-level way: * A three-line comment should document what the code does and give higher level explanations. * A one line comment should detail single code lines or explain specific actions. Example: // // Swap two variables // int a = 1, b = 2, t; // make a copy of a t = a; // perform the swap a = b; b = t; 7 Class variables ------ In order to know whether a variable belongs to a `class` or a `function` we start member variables with "`m_`". Example: public int32_t m_counter; 8 Global variables ------ Similarly, in order to know whether the variable is global or not, we start globals with "`g_`". Example: int g_nplugins; 9 Capitalization ------ The naming convention is camel-cased "Unix" style, i.e. always lower case. Words are separated by underscores. Example: int32_t g_global_bean_counter; int32_t count_beans(); and not, int32_t GlobalBeanCounter; 10 Packed Structures ------- Packed structures should use the GCC and MSVC-style supported `pragma`: #pragma pack(push,1) struct frame_control { struct fields.... }; #pragma pack(pop) 11 OS-specific macros ------- There's an online wiki which enumerates the different macros for compilers, operating systems, and architectures. It's available at [http://sourceforge.net/p/predef/wiki/Home/](http://sourceforge.net/p/predef/wiki/Home/). Generally speaking we use the operating system page: [http://sourceforge.net/p/predef/wiki/OperatingSystems/](http://sourceforge.net/p/predef/wiki/OperatingSystems/). 12 64-bit constants ------- Put an "LL" at the end of your 64 bit constants. Without the LL, on some platforms the compiler tries to interpret the constant on the right hand side as a long integer instead of a long long and in some platform this generate an error at building time. Example: x=0X00FF00000000000LL 13 Class Declaration ------- Class declarations follow the following sequence 1. contructors and desctuctor 1. public functions 1. public data 1. private functions 1. private data 1. friend declarations Example: class foo { public: foo(); ~foo(); int32_t lonli(); int32_t m_val; private: int32_t temustra(); int32_t m_val2; }; 14 Struct guidelines ------- We think hiding the presence of a pointer makes the code unnecessarily ambiguous and more difficult. Seeing a * in a variable declaration immediately identifies a pointer, which is easier to mentally keep track of! Also we think that defining the struct as a typedef makes forward declarations clunky and find using the C++ style when declaring our structs makes our lives easier. // // Us human parsers find this confusing. // typedef struct _my_struct { u_int16 m_field; } my_struct, *p_my_struct; // // This is easier! // struct my_struct { u_int16 m_field; }; 15 Temporary variables ------- Since "j" is used less frequently in english prose than "a" or "i", we find that these variables (in hierarchical order) are great for counters: j, k, l, m, n. Example: int32_t j,k; for(j = 0; j < 10; j++) { for(k = 0; k < 10; k++) { int32_t foo = j + k; } } as opposed to: int32_t i,counter; for(i = 0; i < 10; i++) { for(counter = 0; counter < 10; counter++) { int32_t foo = i + counter; } } 16 Error management ------- Error management inside libscap is done through return values, since the scap library is written in C. Error management in the rest of the sysdig user level code base is done through exceptions. We know there's a lot of debate between return values and exceptions. We decided to pick the latter, so please stick with that. ## You Made It! Phew! That's it. Thanks! If we've left anything in the open, feel free to contact us and we'll be happy to get back to you. Also, you can look at the existing code and see how it's done. Have a good one! sysdig-0.26.4/common/000077500000000000000000000000001352731327100144015ustar00rootroot00000000000000sysdig-0.26.4/common/inttypes_win.h000066400000000000000000000173021352731327100173110ustar00rootroot00000000000000// ISO C9x compliant inttypes.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ #error "Use this header only with Microsoft Visual C++ compilers!" #endif // _MSC_VER ] #ifndef _MSC_INTTYPES_H_ // [ #define _MSC_INTTYPES_H_ #if _MSC_VER > 1000 #pragma once #endif #include "stdint.h" // 7.8 Format conversion of integer types typedef struct { intmax_t quot; intmax_t rem; } imaxdiv_t; // 7.8.1 Macros for format specifiers // The fprintf macros for signed integers are: #define PRId8 "d" #define PRIi8 "i" #define PRIdLEAST8 "d" #define PRIiLEAST8 "i" #define PRIdFAST8 "d" #define PRIiFAST8 "i" #define PRId16 "hd" #define PRIi16 "hi" #define PRIdLEAST16 "hd" #define PRIiLEAST16 "hi" #define PRIdFAST16 "hd" #define PRIiFAST16 "hi" #define PRId32 "I32d" #define PRIi32 "I32i" #define PRIdLEAST32 "I32d" #define PRIiLEAST32 "I32i" #define PRIdFAST32 "I32d" #define PRIiFAST32 "I32i" #define PRId64 "I64d" #define PRIi64 "I64i" #define PRIdLEAST64 "I64d" #define PRIiLEAST64 "I64i" #define PRIdFAST64 "I64d" #define PRIiFAST64 "I64i" #define PRIdMAX "I64d" #define PRIiMAX "I64i" #define PRIdPTR "Id" #define PRIiPTR "Ii" // The fprintf macros for unsigned integers are: #define PRIo8 "o" #define PRIu8 "u" #define PRIx8 "x" #define PRIX8 "X" #define PRIoLEAST8 "o" #define PRIuLEAST8 "u" #define PRIxLEAST8 "x" #define PRIXLEAST8 "X" #define PRIoFAST8 "o" #define PRIuFAST8 "u" #define PRIxFAST8 "x" #define PRIXFAST8 "X" #define PRIo16 "ho" #define PRIu16 "hu" #define PRIx16 "hx" #define PRIX16 "hX" #define PRIoLEAST16 "ho" #define PRIuLEAST16 "hu" #define PRIxLEAST16 "hx" #define PRIXLEAST16 "hX" #define PRIoFAST16 "ho" #define PRIuFAST16 "hu" #define PRIxFAST16 "hx" #define PRIXFAST16 "hX" #define PRIo32 "I32o" #define PRIu32 "I32u" #define PRIx32 "I32x" #define PRIX32 "I32X" #define PRIoLEAST32 "I32o" #define PRIuLEAST32 "I32u" #define PRIxLEAST32 "I32x" #define PRIXLEAST32 "I32X" #define PRIoFAST32 "I32o" #define PRIuFAST32 "I32u" #define PRIxFAST32 "I32x" #define PRIXFAST32 "I32X" #define PRIo64 "I64o" #define PRIu64 "I64u" #define PRIx64 "I64x" #define PRIX64 "I64X" #define PRIoLEAST64 "I64o" #define PRIuLEAST64 "I64u" #define PRIxLEAST64 "I64x" #define PRIXLEAST64 "I64X" #define PRIoFAST64 "I64o" #define PRIuFAST64 "I64u" #define PRIxFAST64 "I64x" #define PRIXFAST64 "I64X" #define PRIoMAX "I64o" #define PRIuMAX "I64u" #define PRIxMAX "I64x" #define PRIXMAX "I64X" #define PRIoPTR "Io" #define PRIuPTR "Iu" #define PRIxPTR "Ix" #define PRIXPTR "IX" // The fscanf macros for signed integers are: #define SCNd8 "d" #define SCNi8 "i" #define SCNdLEAST8 "d" #define SCNiLEAST8 "i" #define SCNdFAST8 "d" #define SCNiFAST8 "i" #define SCNd16 "hd" #define SCNi16 "hi" #define SCNdLEAST16 "hd" #define SCNiLEAST16 "hi" #define SCNdFAST16 "hd" #define SCNiFAST16 "hi" #define SCNd32 "ld" #define SCNi32 "li" #define SCNdLEAST32 "ld" #define SCNiLEAST32 "li" #define SCNdFAST32 "ld" #define SCNiFAST32 "li" #define SCNd64 "I64d" #define SCNi64 "I64i" #define SCNdLEAST64 "I64d" #define SCNiLEAST64 "I64i" #define SCNdFAST64 "I64d" #define SCNiFAST64 "I64i" #define SCNdMAX "I64d" #define SCNiMAX "I64i" #ifdef _WIN64 // [ # define SCNdPTR "I64d" # define SCNiPTR "I64i" #else // _WIN64 ][ # define SCNdPTR "ld" # define SCNiPTR "li" #endif // _WIN64 ] // The fscanf macros for unsigned integers are: #define SCNo8 "o" #define SCNu8 "u" #define SCNx8 "x" #define SCNX8 "X" #define SCNoLEAST8 "o" #define SCNuLEAST8 "u" #define SCNxLEAST8 "x" #define SCNXLEAST8 "X" #define SCNoFAST8 "o" #define SCNuFAST8 "u" #define SCNxFAST8 "x" #define SCNXFAST8 "X" #define SCNo16 "ho" #define SCNu16 "hu" #define SCNx16 "hx" #define SCNX16 "hX" #define SCNoLEAST16 "ho" #define SCNuLEAST16 "hu" #define SCNxLEAST16 "hx" #define SCNXLEAST16 "hX" #define SCNoFAST16 "ho" #define SCNuFAST16 "hu" #define SCNxFAST16 "hx" #define SCNXFAST16 "hX" #define SCNo32 "lo" #define SCNu32 "lu" #define SCNx32 "lx" #define SCNX32 "lX" #define SCNoLEAST32 "lo" #define SCNuLEAST32 "lu" #define SCNxLEAST32 "lx" #define SCNXLEAST32 "lX" #define SCNoFAST32 "lo" #define SCNuFAST32 "lu" #define SCNxFAST32 "lx" #define SCNXFAST32 "lX" #define SCNo64 "I64o" #define SCNu64 "I64u" #define SCNx64 "I64x" #define SCNX64 "I64X" #define SCNoLEAST64 "I64o" #define SCNuLEAST64 "I64u" #define SCNxLEAST64 "I64x" #define SCNXLEAST64 "I64X" #define SCNoFAST64 "I64o" #define SCNuFAST64 "I64u" #define SCNxFAST64 "I64x" #define SCNXFAST64 "I64X" #define SCNoMAX "I64o" #define SCNuMAX "I64u" #define SCNxMAX "I64x" #define SCNXMAX "I64X" #ifdef _WIN64 // [ # define SCNoPTR "I64o" # define SCNuPTR "I64u" # define SCNxPTR "I64x" # define SCNXPTR "I64X" #else // _WIN64 ][ # define SCNoPTR "lo" # define SCNuPTR "lu" # define SCNxPTR "lx" # define SCNXPTR "lX" #endif // _WIN64 ] // 7.8.2 Functions for greatest-width integer types // 7.8.2.1 The imaxabs function #define imaxabs _abs64 // 7.8.2.2 The imaxdiv function // This is modified version of div() function from Microsoft's div.c found // in %MSVC.NET%\crt\src\div.c #ifdef STATIC_IMAXDIV // [ static #else // STATIC_IMAXDIV ][ _inline #endif // STATIC_IMAXDIV ] imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) { imaxdiv_t result; result.quot = numer / denom; result.rem = numer % denom; if (numer < 0 && result.rem > 0) { // did division wrong; must fix up ++result.quot; result.rem -= denom; } return result; } // 7.8.2.3 The strtoimax and strtoumax functions #define strtoimax _strtoi64 #define strtoumax _strtoui64 // 7.8.2.4 The wcstoimax and wcstoumax functions #define wcstoimax _wcstoi64 #define wcstoumax _wcstoui64 #endif // _MSC_INTTYPES_H_ ] sysdig-0.26.4/conf/000077500000000000000000000000001352731327100140365ustar00rootroot00000000000000sysdig-0.26.4/conf/dev.conf000066400000000000000000000000511352731327100154570ustar00rootroot00000000000000export SYSDIG_VERSION=0.99.$BUILD_NUMBER sysdig-0.26.4/docker/000077500000000000000000000000001352731327100143605ustar00rootroot00000000000000sysdig-0.26.4/docker/dev/000077500000000000000000000000001352731327100151365ustar00rootroot00000000000000sysdig-0.26.4/docker/dev/Dockerfile000066400000000000000000000142511352731327100171330ustar00rootroot00000000000000FROM debian:unstable MAINTAINER Sysdig ENV SYSDIG_REPOSITORY dev LABEL RUN="docker run -i -t -v /var/run/docker.sock:/host/var/run/docker.sock -v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro --name NAME IMAGE" ENV SYSDIG_HOST_ROOT /host ENV HOME /root RUN cp /etc/skel/.bashrc /root && cp /etc/skel/.profile /root ADD http://download.draios.com/apt-draios-priority /etc/apt/preferences.d/ RUN apt-get update \ && apt-get upgrade -y \ && apt-get install -y --no-install-recommends \ bash-completion \ bc \ clang-7 \ curl \ dkms \ gnupg2 \ ca-certificates \ gcc \ libc6-dev \ libelf-dev \ libelf1 \ less \ llvm-7 \ procps \ xz-utils \ libmpx2 \ && rm -rf /var/lib/apt/lists/* # gcc 6 is no longer included in debian unstable, but we need it to # build kernel modules on the default debian-based ami used by # kops. So grab copies we've saved from debian snapshots with the # prefix https://snapshot.debian.org/archive/debian/20170517T033514Z # or so. RUN curl -o cpp-6_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/cpp-6_6.3.0-18_amd64.deb \ && curl -o gcc-6-base_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/gcc-6-base_6.3.0-18_amd64.deb \ && curl -o gcc-6_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/gcc-6_6.3.0-18_amd64.deb \ && curl -o libasan3_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libasan3_6.3.0-18_amd64.deb \ && curl -o libcilkrts5_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libcilkrts5_6.3.0-18_amd64.deb \ && curl -o libgcc-6-dev_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libgcc-6-dev_6.3.0-18_amd64.deb \ && curl -o libubsan0_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libubsan0_6.3.0-18_amd64.deb \ && curl -o libmpfr4_3.1.3-2_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libmpfr4_3.1.3-2_amd64.deb \ && curl -o libisl15_0.18-1_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libisl15_0.18-1_amd64.deb \ && dpkg -i cpp-6_6.3.0-18_amd64.deb gcc-6-base_6.3.0-18_amd64.deb gcc-6_6.3.0-18_amd64.deb libasan3_6.3.0-18_amd64.deb libcilkrts5_6.3.0-18_amd64.deb libgcc-6-dev_6.3.0-18_amd64.deb libubsan0_6.3.0-18_amd64.deb libmpfr4_3.1.3-2_amd64.deb libisl15_0.18-1_amd64.deb \ && rm -f cpp-6_6.3.0-18_amd64.deb gcc-6-base_6.3.0-18_amd64.deb gcc-6_6.3.0-18_amd64.deb libasan3_6.3.0-18_amd64.deb libcilkrts5_6.3.0-18_amd64.deb libgcc-6-dev_6.3.0-18_amd64.deb libubsan0_6.3.0-18_amd64.deb libmpfr4_3.1.3-2_amd64.deb libisl15_0.18-1_amd64.deb # gcc 5 is no longer included in debian unstable, but we need it to # build centos kernels, which are 3.x based and explicitly want a gcc # version 3, 4, or 5 compiler. So grab copies we've saved from debian # snapshots with the prefix https://snapshot.debian.org/archive/debian/20190122T000000Z. RUN curl -o cpp-5_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/cpp-5_5.5.0-12_amd64.deb \ && curl -o gcc-5-base_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-5-base_5.5.0-12_amd64.deb \ && curl -o gcc-5_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-5_5.5.0-12_amd64.deb \ && curl -o libasan2_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/libasan2_5.5.0-12_amd64.deb \ && curl -o libgcc-5-dev_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/libgcc-5-dev_5.5.0-12_amd64.deb \ && curl -o libisl15_0.18-4_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/libisl15_0.18-4_amd64.deb \ && curl -o libmpx0_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/libmpx0_5.5.0-12_amd64.deb \ && dpkg -i cpp-5_5.5.0-12_amd64.deb gcc-5-base_5.5.0-12_amd64.deb gcc-5_5.5.0-12_amd64.deb libasan2_5.5.0-12_amd64.deb libgcc-5-dev_5.5.0-12_amd64.deb libisl15_0.18-4_amd64.deb libmpx0_5.5.0-12_amd64.deb \ && rm -f cpp-5_5.5.0-12_amd64.deb gcc-5-base_5.5.0-12_amd64.deb gcc-5_5.5.0-12_amd64.deb libasan2_5.5.0-12_amd64.deb libgcc-5-dev_5.5.0-12_amd64.deb libisl15_0.18-4_amd64.deb libmpx0_5.5.0-12_amd64.deb # Since our base Debian image ships with GCC 7 which breaks older kernels, revert the # default to gcc-5. RUN rm -rf /usr/bin/gcc && ln -s /usr/bin/gcc-5 /usr/bin/gcc RUN rm -rf /usr/bin/clang \ && rm -rf /usr/bin/llc \ && ln -s /usr/bin/clang-7 /usr/bin/clang \ && ln -s /usr/bin/llc-7 /usr/bin/llc RUN curl -s https://s3.amazonaws.com/download.draios.com/DRAIOS-GPG-KEY.public | apt-key add - \ && curl -s -o /etc/apt/sources.list.d/draios.list http://download.draios.com/$SYSDIG_REPOSITORY/deb/draios.list \ && apt-get update \ && apt-get install -y --no-install-recommends sysdig \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # Some base images have an empty /lib/modules by default # If it's not empty, docker build will fail instead of # silently overwriting the existing directory RUN rm -df /lib/modules \ && ln -s $SYSDIG_HOST_ROOT/lib/modules /lib/modules # debian:unstable head contains binutils 2.31, which generates # binaries that are incompatible with kernels < 4.16. So manually # forcibly install binutils 2.30-22 instead. RUN curl -s -o binutils_2.30-22_amd64.deb http://snapshot.debian.org/archive/debian/20180622T211149Z/pool/main/b/binutils/binutils_2.30-22_amd64.deb \ && curl -s -o libbinutils_2.30-22_amd64.deb http://snapshot.debian.org/archive/debian/20180622T211149Z/pool/main/b/binutils/libbinutils_2.30-22_amd64.deb \ && curl -s -o binutils-x86-64-linux-gnu_2.30-22_amd64.deb http://snapshot.debian.org/archive/debian/20180622T211149Z/pool/main/b/binutils/binutils-x86-64-linux-gnu_2.30-22_amd64.deb \ && curl -s -o binutils-common_2.30-22_amd64.deb http://snapshot.debian.org/archive/debian/20180622T211149Z/pool/main/b/binutils/binutils-common_2.30-22_amd64.deb \ && dpkg -i *binutils*.deb COPY ./docker-entrypoint.sh / ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["bash"] sysdig-0.26.4/docker/dev/docker-entrypoint.sh000077500000000000000000000015101352731327100211520ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # #set -e echo "* Setting up /usr/src links from host" for i in $(ls $SYSDIG_HOST_ROOT/usr/src) do ln -s $SYSDIG_HOST_ROOT/usr/src/$i /usr/src/$i done /usr/bin/sysdig-probe-loader exec "$@" sysdig-0.26.4/docker/ebpf-probe-builder/000077500000000000000000000000001352731327100200255ustar00rootroot00000000000000sysdig-0.26.4/docker/ebpf-probe-builder/Dockerfile000066400000000000000000000010751352731327100220220ustar00rootroot00000000000000FROM debian:unstable MAINTAINER Sysdig # Based on the sysdig container, used for building eBPF probe RUN apt-get update \ && apt-get dist-upgrade -y \ && apt-get install -y --no-install-recommends \ clang-7 \ gcc \ libelf-dev \ libelf1 \ llvm-7 \ make \ && rm -rf /var/lib/apt/lists/* # Use clang-7 as the default clang RUN rm -rf /usr/bin/clang \ && rm -rf /usr/bin/llc \ && ln -s /usr/bin/clang-7 /usr/bin/clang \ && ln -s /usr/bin/llc-7 /usr/bin/llc COPY ./probe-builder-entrypoint.sh / ENTRYPOINT ["/probe-builder-entrypoint.sh"] sysdig-0.26.4/docker/ebpf-probe-builder/build_bpf_probe.sh000077500000000000000000000033061352731327100235030ustar00rootroot00000000000000#!/bin/sh set -eu # Defaults DRIVER_DIR=/opt/draios/src/draios-agent-0.1.1dev KERNEL_DIR=/lib/modules/$(uname -r)/build OUT_DIR=${HOME}/.sysdig usage() { echo "build_bpf_probe [-d ] [-k ] [-o ]" } # Options parsing while [ -n "${1-}" ]; do case $1 in -d | --driver ) shift DRIVER_DIR=$1 ;; -k | --kernel ) shift KERNEL_DIR=$1 ;; -o | --output ) shift OUT_DIR=$1 ;; -h | --help ) usage exit ;; * ) usage exit 1 ;; esac shift done mkdir -p ${HOME}/.sysdig # # Mapped volumes: # - ${OUT_DIR}: The directory that the probe gets put in. Defaults to ~/.sysdig # - ${DRIVER_DIR}: The prepared bpf driver code that gets written by the installer # - ${KERNEL_DIR}: The kmod build directory for the target kernel. # - /lib/modules: Unfortunately, on some distros (Debian / Ubuntu), there are # additional support directories (such as a -commmon counterpart to -amd64) which # need to be accessible for the makefile # - /usr: As with the above, on Debian based systems the /lib/modules tree will have # symlinks into /usr/lib/linux-kbuild* and these directories need to be present. docker build -t ebpf-probe-builder:latest --pull . docker images -q -f 'dangling=true' | xargs --no-run-if-empty docker rmi -f docker run --rm -i -v ${OUT_DIR}:/out -v ${DRIVER_DIR}:/driver -v ${KERNEL_DIR}:/kernel -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro -e BPF_PROBE_FILENAME=bpf_probe.o ebpf-probe-builder:latest echo "Probe is in ${OUT_DIR}/" sysdig-0.26.4/docker/ebpf-probe-builder/probe-builder-entrypoint.sh000077500000000000000000000026531352731327100253360ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2019 Draios Inc dba Sysdig. # # This file is part of sysdig. # # 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. # # # Simple script to build the BPF probe. Assumes that all the dependencies # and requirements are already satisfied (as they are in the accompanying # docker container) # set -exu echo "* Building probe ${BPF_PROBE_FILENAME}" # On some distros, the modules dir links into /usr/src, so we need to make sure # we have that sorted so we can build properly for i in $(ls /host/usr/src); do ln -s /host/usr/src/$i /usr/src/$i done # Again, on some distros, we need to populate the /lib/modules directory # because the kernel header info is split among several subdirs mkdir -p /lib/modules for i in $(ls /host/lib/modules); do ln -s /host/lib/modules/$i /lib/modules/$i done cd /driver/bpf echo "Building bpf" KERNELDIR=/kernel make echo "** Done building probe" cp probe.o /out/${BPF_PROBE_FILENAME} sysdig-0.26.4/docker/local/000077500000000000000000000000001352731327100154525ustar00rootroot00000000000000sysdig-0.26.4/docker/local/Dockerfile000066400000000000000000000136621352731327100174540ustar00rootroot00000000000000FROM debian:unstable MAINTAINER Sysdig ENV SYSDIG_VERSION 0.1.1dev LABEL RUN="docker run -i -t -v /var/run/docker.sock:/host/var/run/docker.sock -v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro --name NAME IMAGE" ENV SYSDIG_HOST_ROOT /host ENV HOME /root RUN cp /etc/skel/.bashrc /root && cp /etc/skel/.profile /root ADD http://download.draios.com/apt-draios-priority /etc/apt/preferences.d/ RUN apt-get update \ && apt-get upgrade -y \ && apt-get install -y --no-install-recommends \ bash-completion \ bc \ clang-7 \ curl \ dkms \ gnupg2 \ ca-certificates \ gcc \ libc6-dev \ libelf-dev \ libelf1 \ less \ llvm-7 \ procps \ xz-utils \ libmpx2 \ && rm -rf /var/lib/apt/lists/* # gcc 6 is no longer included in debian unstable, but we need it to # build kernel modules on the default debian-based ami used by # kops. So grab copies we've saved from debian snapshots with the # prefix https://snapshot.debian.org/archive/debian/20170517T033514Z # or so. RUN curl -o cpp-6_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/cpp-6_6.3.0-18_amd64.deb \ && curl -o gcc-6-base_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/gcc-6-base_6.3.0-18_amd64.deb \ && curl -o gcc-6_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/gcc-6_6.3.0-18_amd64.deb \ && curl -o libasan3_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libasan3_6.3.0-18_amd64.deb \ && curl -o libcilkrts5_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libcilkrts5_6.3.0-18_amd64.deb \ && curl -o libgcc-6-dev_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libgcc-6-dev_6.3.0-18_amd64.deb \ && curl -o libubsan0_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libubsan0_6.3.0-18_amd64.deb \ && curl -o libmpfr4_3.1.3-2_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libmpfr4_3.1.3-2_amd64.deb \ && curl -o libisl15_0.18-1_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libisl15_0.18-1_amd64.deb \ && dpkg -i cpp-6_6.3.0-18_amd64.deb gcc-6-base_6.3.0-18_amd64.deb gcc-6_6.3.0-18_amd64.deb libasan3_6.3.0-18_amd64.deb libcilkrts5_6.3.0-18_amd64.deb libgcc-6-dev_6.3.0-18_amd64.deb libubsan0_6.3.0-18_amd64.deb libmpfr4_3.1.3-2_amd64.deb libisl15_0.18-1_amd64.deb \ && rm -f cpp-6_6.3.0-18_amd64.deb gcc-6-base_6.3.0-18_amd64.deb gcc-6_6.3.0-18_amd64.deb libasan3_6.3.0-18_amd64.deb libcilkrts5_6.3.0-18_amd64.deb libgcc-6-dev_6.3.0-18_amd64.deb libubsan0_6.3.0-18_amd64.deb libmpfr4_3.1.3-2_amd64.deb libisl15_0.18-1_amd64.deb # gcc 5 is no longer included in debian unstable, but we need it to # build centos kernels, which are 3.x based and explicitly want a gcc # version 3, 4, or 5 compiler. So grab copies we've saved from debian # snapshots with the prefix https://snapshot.debian.org/archive/debian/20190122T000000Z. RUN curl -o cpp-5_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/cpp-5_5.5.0-12_amd64.deb \ && curl -o gcc-5-base_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-5-base_5.5.0-12_amd64.deb \ && curl -o gcc-5_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-5_5.5.0-12_amd64.deb \ && curl -o libasan2_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/libasan2_5.5.0-12_amd64.deb \ && curl -o libgcc-5-dev_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/libgcc-5-dev_5.5.0-12_amd64.deb \ && curl -o libisl15_0.18-4_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/libisl15_0.18-4_amd64.deb \ && curl -o libmpx0_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/libmpx0_5.5.0-12_amd64.deb \ && dpkg -i cpp-5_5.5.0-12_amd64.deb gcc-5-base_5.5.0-12_amd64.deb gcc-5_5.5.0-12_amd64.deb libasan2_5.5.0-12_amd64.deb libgcc-5-dev_5.5.0-12_amd64.deb libisl15_0.18-4_amd64.deb libmpx0_5.5.0-12_amd64.deb \ && rm -f cpp-5_5.5.0-12_amd64.deb gcc-5-base_5.5.0-12_amd64.deb gcc-5_5.5.0-12_amd64.deb libasan2_5.5.0-12_amd64.deb libgcc-5-dev_5.5.0-12_amd64.deb libisl15_0.18-4_amd64.deb libmpx0_5.5.0-12_amd64.deb # Since our base Debian image ships with GCC 7 which breaks older kernels, revert the # default to gcc-5. RUN rm -rf /usr/bin/gcc && ln -s /usr/bin/gcc-5 /usr/bin/gcc RUN rm -rf /usr/bin/clang \ && rm -rf /usr/bin/llc \ && ln -s /usr/bin/clang-7 /usr/bin/clang \ && ln -s /usr/bin/llc-7 /usr/bin/llc # Some base images have an empty /lib/modules by default # If it's not empty, docker build will fail instead of # silently overwriting the existing directory RUN rm -df /lib/modules \ && ln -s $SYSDIG_HOST_ROOT/lib/modules /lib/modules ADD sysdig-${SYSDIG_VERSION}-x86_64.deb / RUN dpkg -i /sysdig-${SYSDIG_VERSION}-x86_64.deb # debian:unstable head contains binutils 2.31, which generates # binaries that are incompatible with kernels < 4.16. So manually # forcibly install binutils 2.30-22 instead. RUN curl -s -o binutils_2.30-22_amd64.deb http://snapshot.debian.org/archive/debian/20180622T211149Z/pool/main/b/binutils/binutils_2.30-22_amd64.deb \ && curl -s -o libbinutils_2.30-22_amd64.deb http://snapshot.debian.org/archive/debian/20180622T211149Z/pool/main/b/binutils/libbinutils_2.30-22_amd64.deb \ && curl -s -o binutils-x86-64-linux-gnu_2.30-22_amd64.deb http://snapshot.debian.org/archive/debian/20180622T211149Z/pool/main/b/binutils/binutils-x86-64-linux-gnu_2.30-22_amd64.deb \ && curl -s -o binutils-common_2.30-22_amd64.deb http://snapshot.debian.org/archive/debian/20180622T211149Z/pool/main/b/binutils/binutils-common_2.30-22_amd64.deb \ && dpkg -i *binutils*.deb COPY ./docker-entrypoint.sh / ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["bash"] sysdig-0.26.4/docker/local/docker-entrypoint.sh000077500000000000000000000015071352731327100214740ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # #set -e echo "* Setting up /usr/src links from host" for i in $(ls $SYSDIG_HOST_ROOT/usr/src) do ln -s $SYSDIG_HOST_ROOT/usr/src/$i /usr/src/$i done /usr/bin/sysdig-probe-loader exec "$@" sysdig-0.26.4/docker/stable/000077500000000000000000000000001352731327100156325ustar00rootroot00000000000000sysdig-0.26.4/docker/stable/Dockerfile000066400000000000000000000142551352731327100176330ustar00rootroot00000000000000FROM debian:unstable MAINTAINER Sysdig ENV SYSDIG_REPOSITORY stable LABEL RUN="docker run -i -t -v /var/run/docker.sock:/host/var/run/docker.sock -v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro --name NAME IMAGE" ENV SYSDIG_HOST_ROOT /host ENV HOME /root RUN cp /etc/skel/.bashrc /root && cp /etc/skel/.profile /root ADD http://download.draios.com/apt-draios-priority /etc/apt/preferences.d/ RUN apt-get update \ && apt-get upgrade -y \ && apt-get install -y --no-install-recommends \ bash-completion \ bc \ clang-7 \ curl \ dkms \ gnupg2 \ ca-certificates \ gcc \ libc6-dev \ libelf-dev \ libelf1 \ less \ llvm-7 \ procps \ xz-utils \ libmpx2 \ && rm -rf /var/lib/apt/lists/* # gcc 6 is no longer included in debian unstable, but we need it to # build kernel modules on the default debian-based ami used by # kops. So grab copies we've saved from debian snapshots with the # prefix https://snapshot.debian.org/archive/debian/20170517T033514Z # or so. RUN curl -o cpp-6_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/cpp-6_6.3.0-18_amd64.deb \ && curl -o gcc-6-base_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/gcc-6-base_6.3.0-18_amd64.deb \ && curl -o gcc-6_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/gcc-6_6.3.0-18_amd64.deb \ && curl -o libasan3_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libasan3_6.3.0-18_amd64.deb \ && curl -o libcilkrts5_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libcilkrts5_6.3.0-18_amd64.deb \ && curl -o libgcc-6-dev_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libgcc-6-dev_6.3.0-18_amd64.deb \ && curl -o libubsan0_6.3.0-18_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libubsan0_6.3.0-18_amd64.deb \ && curl -o libmpfr4_3.1.3-2_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libmpfr4_3.1.3-2_amd64.deb \ && curl -o libisl15_0.18-1_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-6-debs/libisl15_0.18-1_amd64.deb \ && dpkg -i cpp-6_6.3.0-18_amd64.deb gcc-6-base_6.3.0-18_amd64.deb gcc-6_6.3.0-18_amd64.deb libasan3_6.3.0-18_amd64.deb libcilkrts5_6.3.0-18_amd64.deb libgcc-6-dev_6.3.0-18_amd64.deb libubsan0_6.3.0-18_amd64.deb libmpfr4_3.1.3-2_amd64.deb libisl15_0.18-1_amd64.deb \ && rm -f cpp-6_6.3.0-18_amd64.deb gcc-6-base_6.3.0-18_amd64.deb gcc-6_6.3.0-18_amd64.deb libasan3_6.3.0-18_amd64.deb libcilkrts5_6.3.0-18_amd64.deb libgcc-6-dev_6.3.0-18_amd64.deb libubsan0_6.3.0-18_amd64.deb libmpfr4_3.1.3-2_amd64.deb libisl15_0.18-1_amd64.deb # gcc 5 is no longer included in debian unstable, but we need it to # build centos kernels, which are 3.x based and explicitly want a gcc # version 3, 4, or 5 compiler. So grab copies we've saved from debian # snapshots with the prefix https://snapshot.debian.org/archive/debian/20190122T000000Z. RUN curl -o cpp-5_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/cpp-5_5.5.0-12_amd64.deb \ && curl -o gcc-5-base_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-5-base_5.5.0-12_amd64.deb \ && curl -o gcc-5_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/gcc-5_5.5.0-12_amd64.deb \ && curl -o libasan2_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/libasan2_5.5.0-12_amd64.deb \ && curl -o libgcc-5-dev_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/libgcc-5-dev_5.5.0-12_amd64.deb \ && curl -o libisl15_0.18-4_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/libisl15_0.18-4_amd64.deb \ && curl -o libmpx0_5.5.0-12_amd64.deb https://s3.amazonaws.com/download.draios.com/dependencies/libmpx0_5.5.0-12_amd64.deb \ && dpkg -i cpp-5_5.5.0-12_amd64.deb gcc-5-base_5.5.0-12_amd64.deb gcc-5_5.5.0-12_amd64.deb libasan2_5.5.0-12_amd64.deb libgcc-5-dev_5.5.0-12_amd64.deb libisl15_0.18-4_amd64.deb libmpx0_5.5.0-12_amd64.deb \ && rm -f cpp-5_5.5.0-12_amd64.deb gcc-5-base_5.5.0-12_amd64.deb gcc-5_5.5.0-12_amd64.deb libasan2_5.5.0-12_amd64.deb libgcc-5-dev_5.5.0-12_amd64.deb libisl15_0.18-4_amd64.deb libmpx0_5.5.0-12_amd64.deb # Since our base Debian image ships with GCC 7 which breaks older kernels, revert the # default to gcc-5. RUN rm -rf /usr/bin/gcc && ln -s /usr/bin/gcc-5 /usr/bin/gcc RUN rm -rf /usr/bin/clang \ && rm -rf /usr/bin/llc \ && ln -s /usr/bin/clang-7 /usr/bin/clang \ && ln -s /usr/bin/llc-7 /usr/bin/llc RUN curl -s https://s3.amazonaws.com/download.draios.com/DRAIOS-GPG-KEY.public | apt-key add - \ && curl -s -o /etc/apt/sources.list.d/draios.list http://download.draios.com/$SYSDIG_REPOSITORY/deb/draios.list \ && apt-get update \ && apt-get install -y --no-install-recommends sysdig \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # Some base images have an empty /lib/modules by default # If it's not empty, docker build will fail instead of # silently overwriting the existing directory RUN rm -df /lib/modules \ && ln -s $SYSDIG_HOST_ROOT/lib/modules /lib/modules # debian:unstable head contains binutils 2.31, which generates # binaries that are incompatible with kernels < 4.16. So manually # forcibly install binutils 2.30-22 instead. RUN curl -s -o binutils_2.30-22_amd64.deb http://snapshot.debian.org/archive/debian/20180622T211149Z/pool/main/b/binutils/binutils_2.30-22_amd64.deb \ && curl -s -o libbinutils_2.30-22_amd64.deb http://snapshot.debian.org/archive/debian/20180622T211149Z/pool/main/b/binutils/libbinutils_2.30-22_amd64.deb \ && curl -s -o binutils-x86-64-linux-gnu_2.30-22_amd64.deb http://snapshot.debian.org/archive/debian/20180622T211149Z/pool/main/b/binutils/binutils-x86-64-linux-gnu_2.30-22_amd64.deb \ && curl -s -o binutils-common_2.30-22_amd64.deb http://snapshot.debian.org/archive/debian/20180622T211149Z/pool/main/b/binutils/binutils-common_2.30-22_amd64.deb \ && dpkg -i *binutils*.deb COPY ./docker-entrypoint.sh / ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["bash"] sysdig-0.26.4/docker/stable/docker-entrypoint.sh000077500000000000000000000015071352731327100216540ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # #set -e echo "* Setting up /usr/src links from host" for i in $(ls $SYSDIG_HOST_ROOT/usr/src) do ln -s $SYSDIG_HOST_ROOT/usr/src/$i /usr/src/$i done /usr/bin/sysdig-probe-loader exec "$@" sysdig-0.26.4/driver/000077500000000000000000000000001352731327100144045ustar00rootroot00000000000000sysdig-0.26.4/driver/CMakeLists.txt000066400000000000000000000065341352731327100171540ustar00rootroot00000000000000# # Copyright (c) 2013-2018 Draios Inc. dba Sysdig. # # This file is dual licensed under either the MIT or GPL 2. See # MIT.txt or GPL.txt for full copies of the license. # option(BUILD_DRIVER "Build the driver on Linux" ON) option(ENABLE_DKMS "Enable DKMS on Linux" ON) # The driver build process is somewhat involved because we use the same # sources for building the driver locally and for shipping as a DKMS module. # # We need a single directory with the following files inside: # - all the driver *.c/*.h sources # - Makefile generated from Makefile.in # - driver_config.h generated from driver_config.h.in # # The Makefile _must_ be called just Makefile (and not e.g. Makefile.dkms) # because of the module build process, which looks like this: # 1. The user (or some script) runs make in our driver directory # 2. Our Makefile runs the Makefile from kernel sources/headers # 3. The kernel Makefile calls our original Makefile again, with options that # trigger the actual build. This step cannot know that our Makefile has # a different name. # # (DKMS needs a Makefile called Makefile as well). # # The files need to be in a single directory because we cannot know where # the sources will be built (especially by DKMS) so we cannot put _any_ paths # in the Makefile. # # The chosen directory must not be ${CMAKE_CURRENT_BINARY_DIR} because CMake # puts its own generated Makefile in there, so we (arbitrarily) choose # ${CMAKE_CURRENT_BINARY_DIR}/src. To maintain compatibility with older versions, # after the build we copy the compiled module one directory up, # to ${CMAKE_CURRENT_BINARY_DIR}. configure_file(dkms.conf.in src/dkms.conf) configure_file(Makefile.in src/Makefile) configure_file(driver_config.h.in src/driver_config.h) set(DRIVER_SOURCES dynamic_params_table.c event_table.c fillers_table.c flags_table.c main.c ppm.h ppm_events.c ppm_events.h ppm_events_public.h ppm_fillers.c ppm_fillers.h ppm_flag_helpers.h ppm_ringbuffer.h ppm_syscall.h syscall_table.c ppm_cputime.c ppm_compat_unistd_32.h ppm_version.h ) foreach(FILENAME IN LISTS DRIVER_SOURCES) configure_file(${FILENAME} src/${FILENAME} COPYONLY) endforeach() # make can be self-referenced as $(MAKE) only from Makefiles but this # triggers syntax errors with other generators such as Ninja if(${CMAKE_GENERATOR} STREQUAL "Unix Makefiles") set(MAKE_COMMAND "$(MAKE)") else() set(MAKE_COMMAND "make") endif() # This if/else is needed because you currently cannot manipulate dependencies # of built-in targets like "all" in CMake: # http://public.kitware.com/Bug/view.php?id=8438 if(BUILD_DRIVER) add_custom_target(driver ALL COMMAND ${MAKE_COMMAND} COMMAND "${CMAKE_COMMAND}" -E copy_if_different ${PROBE_NAME}.ko "${CMAKE_CURRENT_BINARY_DIR}" WORKING_DIRECTORY src VERBATIM) else() add_custom_target(driver COMMAND ${MAKE_COMMAND} COMMAND "${CMAKE_COMMAND}" -E copy_if_different ${PROBE_NAME}.ko "${CMAKE_CURRENT_BINARY_DIR}" WORKING_DIRECTORY src VERBATIM) endif() add_custom_target(install_driver COMMAND ${MAKE_COMMAND} install DEPENDS driver WORKING_DIRECTORY src VERBATIM) if(ENABLE_DKMS) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/src/Makefile ${CMAKE_CURRENT_BINARY_DIR}/src/dkms.conf ${CMAKE_CURRENT_BINARY_DIR}/src/driver_config.h ${DRIVER_SOURCES} DESTINATION "src/${PACKAGE_NAME}-${PROBE_VERSION}" COMPONENT agent-kmodule) endif() add_subdirectory(bpf) sysdig-0.26.4/driver/GPL2.txt000066400000000000000000000432541352731327100156610ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. sysdig-0.26.4/driver/MIT.txt000066400000000000000000000020571352731327100156020ustar00rootroot00000000000000Copyright (c) 2013-2018 Draios Inc. dba Sysdig Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. sysdig-0.26.4/driver/Makefile.in000066400000000000000000000011401352731327100164450ustar00rootroot00000000000000# # Copyright (c) 2013-2018 Draios Inc. dba Sysdig. # # This file is dual licensed under either the MIT or GPL 2. See # MIT.txt or GPL.txt for full copies of the license. # @PROBE_NAME@-y += main.o dynamic_params_table.o fillers_table.o flags_table.o ppm_events.o ppm_fillers.o event_table.o syscall_table.o ppm_cputime.o obj-m += @PROBE_NAME@.o ccflags-y := @KBUILD_FLAGS@ KERNELDIR ?= /lib/modules/$(shell uname -r)/build TOP := $(shell pwd) all: $(MAKE) -C $(KERNELDIR) M=$(TOP) modules clean: $(MAKE) -C $(KERNELDIR) M=$(TOP) clean install: all $(MAKE) -C $(KERNELDIR) M=$(TOP) modules_install sysdig-0.26.4/driver/bpf/000077500000000000000000000000001352731327100151535ustar00rootroot00000000000000sysdig-0.26.4/driver/bpf/CMakeLists.txt000066400000000000000000000012321352731327100177110ustar00rootroot00000000000000# # Copyright (c) 2013-2018 Draios Inc. dba Sysdig. # # This file is dual licensed under either the MIT or GPL 2. See # MIT.txt or GPL.txt for full copies of the license. # option(BUILD_BPF "Build the BPF driver on Linux" OFF) if(BUILD_BPF) add_custom_target(bpf ALL COMMAND make COMMAND "${CMAKE_COMMAND}" -E copy_if_different probe.o "${CMAKE_CURRENT_BINARY_DIR}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" VERBATIM) endif() install(FILES bpf_helpers.h filler_helpers.h fillers.h Makefile maps.h plumbing_helpers.h probe.c quirks.h ring_helpers.h types.h DESTINATION "src/${PACKAGE_NAME}-${PROBE_VERSION}/bpf" COMPONENT agent-kmodule) sysdig-0.26.4/driver/bpf/Makefile000066400000000000000000000017631352731327100166220ustar00rootroot00000000000000# # Copyright (c) 2013-2018 Draios Inc. dba Sysdig. # # This file is dual licensed under either the MIT or GPL 2. See # MIT.txt or GPL.txt for full copies of the license. # always += probe.o LLC ?= llc CLANG ?= clang KERNELDIR ?= /lib/modules/$(shell uname -r)/build # DEBUG = -DBPF_DEBUG all: $(MAKE) -C $(KERNELDIR) M=$$PWD clean: $(MAKE) -C $(KERNELDIR) M=$$PWD clean @rm -f *~ $(obj)/probe.o: $(src)/probe.c \ $(src)/bpf_helpers.h \ $(src)/filler_helpers.h \ $(src)/fillers.h \ $(src)/maps.h \ $(src)/plumbing_helpers.h \ $(src)/quirks.h \ $(src)/ring_helpers.h \ $(src)/types.h $(CLANG) $(LINUXINCLUDE) \ $(KBUILD_CPPFLAGS) \ $(KBUILD_EXTRA_CPPFLAGS) \ $(DEBUG) \ -D__KERNEL__ \ -D__BPF_TRACING__ \ -Wno-gnu-variable-sized-type-not-at-end \ -Wno-address-of-packed-member \ -fno-jump-tables \ -fno-stack-protector \ -Wno-tautological-compare \ -O2 -g -emit-llvm -c $< -o $(patsubst %.o,%.ll,$@) $(LLC) -march=bpf -filetype=obj -o $@ $(patsubst %.o,%.ll,$@) sysdig-0.26.4/driver/bpf/bpf_helpers.h000066400000000000000000000070201352731327100176140ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifndef __BPF_HELPERS_H #define __BPF_HELPERS_H static void *(*bpf_map_lookup_elem)(void *map, void *key) = (void *)BPF_FUNC_map_lookup_elem; static int (*bpf_map_update_elem)(void *map, void *key, void *value, unsigned long long flags) = (void *)BPF_FUNC_map_update_elem; static int (*bpf_map_delete_elem)(void *map, void *key) = (void *)BPF_FUNC_map_delete_elem; static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) = (void *)BPF_FUNC_probe_read; static unsigned long long (*bpf_ktime_get_ns)(void) = (void *)BPF_FUNC_ktime_get_ns; static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = (void *)BPF_FUNC_trace_printk; static void (*bpf_tail_call)(void *ctx, void *map, int index) = (void *)BPF_FUNC_tail_call; static unsigned long long (*bpf_get_smp_processor_id)(void) = (void *)BPF_FUNC_get_smp_processor_id; static unsigned long long (*bpf_get_current_pid_tgid)(void) = (void *)BPF_FUNC_get_current_pid_tgid; static unsigned long long (*bpf_get_current_uid_gid)(void) = (void *)BPF_FUNC_get_current_uid_gid; static int (*bpf_get_current_comm)(void *buf, int buf_size) = (void *)BPF_FUNC_get_current_comm; static int (*bpf_perf_event_read)(void *map, int index) = (void *)BPF_FUNC_perf_event_read; static int (*bpf_clone_redirect)(void *ctx, int ifindex, int flags) = (void *)BPF_FUNC_clone_redirect; static int (*bpf_redirect)(int ifindex, int flags) = (void *)BPF_FUNC_redirect; static int (*bpf_perf_event_output)(void *ctx, void *map, unsigned long long flags, void *data, int size) = (void *)BPF_FUNC_perf_event_output; static int (*bpf_get_stackid)(void *ctx, void *map, int flags) = (void *)BPF_FUNC_get_stackid; static int (*bpf_probe_write_user)(void *dst, void *src, int size) = (void *)BPF_FUNC_probe_write_user; static int (*bpf_current_task_under_cgroup)(void *map, int index) = (void *)BPF_FUNC_current_task_under_cgroup; static int (*bpf_skb_get_tunnel_key)(void *ctx, void *key, int size, int flags) = (void *)BPF_FUNC_skb_get_tunnel_key; static int (*bpf_skb_set_tunnel_key)(void *ctx, void *key, int size, int flags) = (void *)BPF_FUNC_skb_set_tunnel_key; static int (*bpf_skb_get_tunnel_opt)(void *ctx, void *md, int size) = (void *)BPF_FUNC_skb_get_tunnel_opt; static int (*bpf_skb_set_tunnel_opt)(void *ctx, void *md, int size) = (void *)BPF_FUNC_skb_set_tunnel_opt; static unsigned long long (*bpf_get_prandom_u32)(void) = (void *)BPF_FUNC_get_prandom_u32; static int (*bpf_xdp_adjust_head)(void *ctx, int offset) = (void *)BPF_FUNC_xdp_adjust_head; static int (*bpf_probe_read_str)(void *dst, u64 size, const void *unsafe_ptr) = (void *)BPF_FUNC_probe_read_str; static u64 (*bpf_get_current_task)(void) = (void *)BPF_FUNC_get_current_task; static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = (void *)BPF_FUNC_skb_load_bytes; static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) = (void *)BPF_FUNC_skb_store_bytes; static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) = (void *)BPF_FUNC_l3_csum_replace; static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) = (void *)BPF_FUNC_l4_csum_replace; static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) = (void *)BPF_FUNC_skb_under_cgroup; static int (*bpf_skb_change_head)(void *, int len, int flags) = (void *)BPF_FUNC_skb_change_head; #endif sysdig-0.26.4/driver/bpf/filler_helpers.h000066400000000000000000000611771352731327100203370ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifndef __SYSDIGBPF_HELPERS_H #define __SYSDIGBPF_HELPERS_H #include #include #include #include #include #include #include #include "../ppm_flag_helpers.h" static __always_inline bool in_port_range(uint16_t port, uint16_t min, uint16_t max) { return port >= min && port <= max; } static __always_inline struct file *bpf_fget(int fd) { struct task_struct *task; struct files_struct *files; struct fdtable *fdt; int max_fds; struct file **fds; struct file *fil; task = (struct task_struct *)bpf_get_current_task(); if (!task) return NULL; files = _READ(task->files); if (!files) return NULL; fdt = _READ(files->fdt); if (!fdt) return NULL; max_fds = _READ(fdt->max_fds); if (fd >= max_fds) return NULL; fds = _READ(fdt->fd); fil = _READ(fds[fd]); return fil; } static __always_inline struct socket *bpf_sockfd_lookup(struct filler_data *data, int fd) { struct file *file; const struct file_operations *fop; struct socket *sock; if (!data->settings->socket_file_ops) return NULL; file = bpf_fget(fd); if (!file) return NULL; fop = _READ(file->f_op); if (fop != data->settings->socket_file_ops) return NULL; sock = _READ(file->private_data); return sock; } static __always_inline unsigned long bpf_encode_dev(dev_t dev) { unsigned int major = MAJOR(dev); unsigned int minor = MINOR(dev); return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12); } static __always_inline bool bpf_get_fd_dev_ino(int fd, unsigned long *dev, unsigned long *ino) { struct super_block *sb; struct inode *inode; struct file *file; dev_t kdev; file = bpf_fget(fd); if (!file) return false; inode = _READ(file->f_inode); if (!inode) return false; sb = _READ(inode->i_sb); if (!sb) return false; kdev = _READ(sb->s_dev); *dev = bpf_encode_dev(kdev); *ino = _READ(inode->i_ino); return true; } static __always_inline bool bpf_ipv6_addr_any(const struct in6_addr *a) { const unsigned long *ul = (const unsigned long *)a; return (ul[0] | ul[1]) == 0UL; } static __always_inline bool bpf_getsockname(struct socket *sock, struct sockaddr_storage *addr, int peer) { struct sock *sk; sa_family_t family; sk = _READ(sock->sk); if (!sk) return false; family = _READ(sk->sk_family); switch (family) { case AF_INET: { struct inet_sock *inet = (struct inet_sock *)sk; struct sockaddr_in *sin = (struct sockaddr_in *)addr; sin->sin_family = AF_INET; if (peer) { sin->sin_port = _READ(inet->inet_dport); sin->sin_addr.s_addr = _READ(inet->inet_daddr); } else { u32 addr = _READ(inet->inet_rcv_saddr); if (!addr) addr = _READ(inet->inet_saddr); sin->sin_port = _READ(inet->inet_sport); sin->sin_addr.s_addr = addr; } break; } case AF_INET6: { struct sockaddr_in6 *sin = (struct sockaddr_in6 *)addr; struct inet_sock *inet = (struct inet_sock *)sk; struct ipv6_pinfo { struct in6_addr saddr; }; struct ipv6_pinfo *np = (struct ipv6_pinfo *)_READ(inet->pinet6); sin->sin6_family = AF_INET6; if (peer) { sin->sin6_port = _READ(inet->inet_dport); sin->sin6_addr = _READ(sk->sk_v6_daddr); } else { sin->sin6_addr = _READ(sk->sk_v6_rcv_saddr); if (bpf_ipv6_addr_any(&sin->sin6_addr)) sin->sin6_addr = _READ(np->saddr); sin->sin6_port = _READ(inet->inet_sport); } break; } case AF_UNIX: { struct sockaddr_un *sunaddr = (struct sockaddr_un *)addr; struct unix_sock *u; struct unix_address *addr; if (peer) sk = _READ(((struct unix_sock *)sk)->peer); u = (struct unix_sock *)sk; addr = _READ(u->addr); if (!addr) { sunaddr->sun_family = AF_UNIX; sunaddr->sun_path[0] = 0; } else { unsigned int len = _READ(addr->len); if (len > sizeof(struct sockaddr_storage)) len = sizeof(struct sockaddr_storage); #ifdef BPF_FORBIDS_ZERO_ACCESS if (len > 0) bpf_probe_read(sunaddr, ((len - 1) & 0xff) + 1, addr->name); #else bpf_probe_read(sunaddr, len, addr->name); #endif } break; } default: return false; } return true; } static __always_inline int bpf_addr_to_kernel(void *uaddr, int ulen, struct sockaddr *kaddr) { if (ulen < 0 || ulen > sizeof(struct sockaddr_storage)) return -EINVAL; if (ulen == 0) return 0; #ifdef BPF_FORBIDS_ZERO_ACCESS if (bpf_probe_read(kaddr, ((ulen - 1) & 0xff) + 1, uaddr)) #else if (bpf_probe_read(kaddr, ulen & 0xff, uaddr)) #endif return -EFAULT; return 0; } #define get_buf(x) data->buf[(data->state->tail_ctx.curoff + (x)) & SCRATCH_SIZE_HALF] static __always_inline u32 bpf_compute_snaplen(struct filler_data *data, u32 lookahead_size) { struct sockaddr_storage *sock_address; struct sockaddr_storage *peer_address; u32 res = data->settings->snaplen; struct socket *sock; struct sock *sk; u16 sport; u16 dport; if (data->settings->tracers_enabled && data->state->tail_ctx.evt_type == PPME_SYSCALL_WRITE_X) { struct file *fil; struct inode *f_inode; dev_t i_rdev; fil = bpf_fget(data->fd); if (!fil) return res; f_inode = _READ(fil->f_inode); if (!f_inode) return res; i_rdev = _READ(f_inode->i_rdev); if (i_rdev == PPM_NULL_RDEV) return RW_SNAPLEN_EVENT; } if (!data->settings->do_dynamic_snaplen) return res; if (data->fd == -1) return res; sock = bpf_sockfd_lookup(data, data->fd); if (!sock) return res; sock_address = (struct sockaddr_storage *)data->tmp_scratch; peer_address = (struct sockaddr_storage *)data->tmp_scratch + 1; if (!bpf_getsockname(sock, sock_address, 0)) return res; if (data->state->tail_ctx.evt_type == PPME_SOCKET_SENDTO_X) { unsigned long val; struct sockaddr *usrsockaddr; usrsockaddr = (struct sockaddr *)bpf_syscall_get_argument(data, 4); if (!usrsockaddr) { if (!bpf_getsockname(sock, peer_address, 1)) return res; } else { int addrlen = bpf_syscall_get_argument(data, 5); if (addrlen != 0) { if (bpf_addr_to_kernel(usrsockaddr, addrlen, (struct sockaddr *)peer_address)) return res; } else if (!bpf_getsockname(sock, peer_address, 1)) { return res; } } } else if (data->state->tail_ctx.evt_type == PPME_SOCKET_SENDMSG_X) { struct sockaddr *usrsockaddr; struct user_msghdr mh; unsigned long val; int addrlen; val = bpf_syscall_get_argument(data, 1); if (bpf_probe_read(&mh, sizeof(mh), (void *)val)) { usrsockaddr = NULL; addrlen = 0; } else { usrsockaddr = (struct sockaddr *)mh.msg_name; addrlen = mh.msg_namelen; } if (usrsockaddr && addrlen != 0) { if (bpf_addr_to_kernel(usrsockaddr, addrlen, (struct sockaddr *)peer_address)) return res; } else if (!bpf_getsockname(sock, peer_address, 1)) { return res; } } else if (!bpf_getsockname(sock, peer_address, 1)) { return res; } sk = _READ(sock->sk); if (!sk) return res; sa_family_t family = _READ(sk->sk_family); if (family == AF_INET) { sport = ntohs(((struct sockaddr_in *)sock_address)->sin_port); dport = ntohs(((struct sockaddr_in *)peer_address)->sin_port); } else if (family == AF_INET6) { sport = ntohs(((struct sockaddr_in6 *)sock_address)->sin6_port); dport = ntohs(((struct sockaddr_in6 *)peer_address)->sin6_port); } else { sport = 0; dport = 0; } uint16_t min_port = data->settings->fullcapture_port_range_start; uint16_t max_port = data->settings->fullcapture_port_range_end; if (max_port > 0 && (in_port_range(sport, min_port, max_port) || in_port_range(dport, min_port, max_port))) { /* * Before checking the well-known ports, see if the user has requested * an increased snaplen for the port in question. */ return RW_MAX_FULLCAPTURE_PORT_SNAPLEN; } else if (sport == PPM_PORT_MYSQL || dport == PPM_PORT_MYSQL) { if (lookahead_size >= 5) { if (get_buf(0) == 3 || get_buf(1) == 3 || get_buf(2) == 3 || get_buf(3) == 3 || get_buf(4) == 3) { return 2000; } else if (get_buf(2) == 0 && get_buf(3) == 0) { return 2000; } } } else if (sport == PPM_PORT_POSTGRES || dport == PPM_PORT_POSTGRES) { if (lookahead_size >= 2) { if ((get_buf(0) == 'Q' && get_buf(1) == 0) || /* SimpleQuery command */ (get_buf(0) == 'P' && get_buf(1) == 0) || /* Prepare statement commmand */ (get_buf(4) == 0 && get_buf(5) == 3 && get_buf(6) == 0) || /* startup command */ (get_buf(0) == 'E' && get_buf(1) == 0) /* error or execute command */ ) { return 2000; } } } else if ((lookahead_size >= 4 && get_buf(1) == 0 && get_buf(2) == 0 && get_buf(2) == 0) || /* matches command */ (lookahead_size >= 16 && (*(s32 *)&get_buf(12) == 1 || /* matches header */ *(s32 *)&get_buf(12) == 2001 || *(s32 *)&get_buf(12) == 2002 || *(s32 *)&get_buf(12) == 2003 || *(s32 *)&get_buf(12) == 2004 || *(s32 *)&get_buf(12) == 2005 || *(s32 *)&get_buf(12) == 2006 || *(s32 *)&get_buf(12) == 2007))) { return 2000; } else if (dport == data->settings->statsd_port) { return 2000; } else { if (lookahead_size >= 5) { u32 buf = *(u32 *)&get_buf(0); if (buf == 0x20544547 || // "GET " buf == 0x54534F50 || // "POST" buf == 0x20545550 || // "PUT " buf == 0x454C4544 || // "DELE" buf == 0x43415254 || // "TRAC" buf == 0x4E4E4F43 || // "CONN" buf == 0x4954504F || // "OPTI" (buf == 0x50545448 && data->buf[(data->state->tail_ctx.curoff + 4) & SCRATCH_SIZE_HALF] == '/')) { // "HTTP/" return 2000; } } } return res; } static __always_inline u16 bpf_pack_addr(struct filler_data *data, struct sockaddr *usrsockaddr, int ulen) { u32 ip; u16 port; sa_family_t family = usrsockaddr->sa_family; struct sockaddr_in *usrsockaddr_in; struct sockaddr_in6 *usrsockaddr_in6; struct sockaddr_un *usrsockaddr_un; u16 size; char *dest; int res; switch (family) { case AF_INET: /* * Map the user-provided address to a sockaddr_in */ usrsockaddr_in = (struct sockaddr_in *)usrsockaddr; /* * Retrieve the src address */ ip = usrsockaddr_in->sin_addr.s_addr; port = ntohs(usrsockaddr_in->sin_port); /* * Pack the tuple info in the temporary buffer */ size = 1 + 4 + 2; /* family + ip + port */ data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF] = socket_family_to_scap(family); memcpy(&data->buf[(data->state->tail_ctx.curoff + 1) & SCRATCH_SIZE_HALF], &ip, 4); memcpy(&data->buf[(data->state->tail_ctx.curoff + 5) & SCRATCH_SIZE_HALF], &port, 2); break; case AF_INET6: /* * Map the user-provided address to a sockaddr_in */ usrsockaddr_in6 = (struct sockaddr_in6 *)usrsockaddr; /* * Retrieve the src address */ port = ntohs(usrsockaddr_in6->sin6_port); /* * Pack the tuple info in the temporary buffer */ size = 1 + 16 + 2; /* family + ip + port */ data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF] = socket_family_to_scap(family); memcpy(&data->buf[(data->state->tail_ctx.curoff + 1) & SCRATCH_SIZE_HALF], usrsockaddr_in6->sin6_addr.s6_addr, 16); memcpy(&data->buf[(data->state->tail_ctx.curoff + 17) & SCRATCH_SIZE_HALF], &port, 2); break; case AF_UNIX: /* * Map the user-provided address to a sockaddr_in */ usrsockaddr_un = (struct sockaddr_un *)usrsockaddr; /* * Put a 0 at the end of struct sockaddr_un because * the user might not have considered it in the length */ if (ulen == sizeof(struct sockaddr_storage)) ((char *)usrsockaddr_un)[(ulen - 1) & SCRATCH_SIZE_MAX] = 0; else ((char *)usrsockaddr_un)[ulen & SCRATCH_SIZE_MAX] = 0; /* * Pack the data into the target buffer */ size = 1; data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF] = socket_family_to_scap(family); res = bpf_probe_read_str(&data->buf[(data->state->tail_ctx.curoff + 1) & SCRATCH_SIZE_HALF], UNIX_PATH_MAX, usrsockaddr_un->sun_path); size += res; break; default: size = 0; break; } return size; } static __always_inline long bpf_fd_to_socktuple(struct filler_data *data, int fd, struct sockaddr *usrsockaddr, int ulen, bool use_userdata, bool is_inbound, char *tmp_area) { struct sockaddr_storage *sock_address; struct sockaddr_storage *peer_address; unsigned short family; struct socket *sock; struct sock *sk; long size = 0; sock = bpf_sockfd_lookup(data, fd); if (!sock) return 0; sock_address = (struct sockaddr_storage *)tmp_area; peer_address = (struct sockaddr_storage *)tmp_area + 1; if (!bpf_getsockname(sock, sock_address, 0)) return 0; sk = _READ(sock->sk); if (!sk) return 0; family = _READ(sk->sk_family); switch (family) { case AF_INET: { u32 sip; u32 dip; u16 sport; u16 dport; if (!use_userdata) { if (bpf_getsockname(sock, peer_address, 1)) { if (is_inbound) { sip = ((struct sockaddr_in *)peer_address)->sin_addr.s_addr; sport = ntohs(((struct sockaddr_in *)peer_address)->sin_port); dip = ((struct sockaddr_in *)sock_address)->sin_addr.s_addr; dport = ntohs(((struct sockaddr_in *)sock_address)->sin_port); } else { sip = ((struct sockaddr_in *)sock_address)->sin_addr.s_addr; sport = ntohs(((struct sockaddr_in *)sock_address)->sin_port); dip = ((struct sockaddr_in *)peer_address)->sin_addr.s_addr; dport = ntohs(((struct sockaddr_in *)peer_address)->sin_port); } } else { sip = 0; sport = 0; dip = 0; dport = 0; } } else { struct sockaddr_in *usrsockaddr_in = (struct sockaddr_in *)usrsockaddr; if (is_inbound) { sip = usrsockaddr_in->sin_addr.s_addr; sport = ntohs(usrsockaddr_in->sin_port); dip = ((struct sockaddr_in *)sock_address)->sin_addr.s_addr; dport = ntohs(((struct sockaddr_in *)sock_address)->sin_port); } else { sip = ((struct sockaddr_in *)sock_address)->sin_addr.s_addr; sport = ntohs(((struct sockaddr_in *)sock_address)->sin_port); dip = usrsockaddr_in->sin_addr.s_addr; dport = ntohs(usrsockaddr_in->sin_port); } } size = 1 + 4 + 4 + 2 + 2; data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF] = socket_family_to_scap(family); memcpy(&data->buf[(data->state->tail_ctx.curoff + 1) & SCRATCH_SIZE_HALF], &sip, 4); memcpy(&data->buf[(data->state->tail_ctx.curoff + 5) & SCRATCH_SIZE_HALF], &sport, 2); memcpy(&data->buf[(data->state->tail_ctx.curoff + 7) & SCRATCH_SIZE_HALF], &dip, 4); memcpy(&data->buf[(data->state->tail_ctx.curoff + 11) & SCRATCH_SIZE_HALF], &dport, 2); break; } case AF_INET6: { u8 *sip6; u8 *dip6; u16 sport; u16 dport; if (!use_userdata) { if (bpf_getsockname(sock, peer_address, 1)) { if (is_inbound) { sip6 = ((struct sockaddr_in6 *)peer_address)->sin6_addr.s6_addr; sport = ntohs(((struct sockaddr_in6 *)peer_address)->sin6_port); dip6 = ((struct sockaddr_in6 *)sock_address)->sin6_addr.s6_addr; dport = ntohs(((struct sockaddr_in6 *)sock_address)->sin6_port); } else { sip6 = ((struct sockaddr_in6 *)sock_address)->sin6_addr.s6_addr; sport = ntohs(((struct sockaddr_in6 *)sock_address)->sin6_port); dip6 = ((struct sockaddr_in6 *)peer_address)->sin6_addr.s6_addr; dport = ntohs(((struct sockaddr_in6 *)peer_address)->sin6_port); } } else { memset(peer_address, 0, 16); sip6 = (u8 *)peer_address; dip6 = (u8 *)peer_address; sport = 0; dport = 0; } } else { /* * Map the user-provided address to a sockaddr_in6 */ struct sockaddr_in6 *usrsockaddr_in6 = (struct sockaddr_in6 *)usrsockaddr; if (is_inbound) { sip6 = usrsockaddr_in6->sin6_addr.s6_addr; sport = ntohs(usrsockaddr_in6->sin6_port); dip6 = ((struct sockaddr_in6 *)sock_address)->sin6_addr.s6_addr; dport = ntohs(((struct sockaddr_in6 *)sock_address)->sin6_port); } else { sip6 = ((struct sockaddr_in6 *)sock_address)->sin6_addr.s6_addr; sport = ntohs(((struct sockaddr_in6 *)sock_address)->sin6_port); dip6 = usrsockaddr_in6->sin6_addr.s6_addr; dport = ntohs(usrsockaddr_in6->sin6_port); } } /* * Pack the tuple info in the temporary buffer */ size = 1 + 16 + 16 + 2 + 2; /* family + sip + dip + sport + dport */ data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF] = socket_family_to_scap(family); memcpy(&data->buf[(data->state->tail_ctx.curoff + 1) & SCRATCH_SIZE_HALF], sip6, 16); memcpy(&data->buf[(data->state->tail_ctx.curoff + 17) & SCRATCH_SIZE_HALF], &sport, 2); memcpy(&data->buf[(data->state->tail_ctx.curoff + 19) & SCRATCH_SIZE_HALF], dip6, 16); memcpy(&data->buf[(data->state->tail_ctx.curoff + 35) & SCRATCH_SIZE_HALF], &dport, 2); break; } case AF_UNIX: { /* * Retrieve the addresses */ struct unix_sock *us = (struct unix_sock *)sk; struct sock *speer = _READ(us->peer); char *us_name; data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF] = socket_family_to_scap(family); if (is_inbound) { memcpy(&data->buf[(data->state->tail_ctx.curoff + 1) & SCRATCH_SIZE_HALF], &us, 8); memcpy(&data->buf[(data->state->tail_ctx.curoff + 1 + 8) & SCRATCH_SIZE_HALF], &speer, 8); } else { memcpy(&data->buf[(data->state->tail_ctx.curoff + 1) & SCRATCH_SIZE_HALF], &speer, 8); memcpy(&data->buf[(data->state->tail_ctx.curoff + 1 + 8) & SCRATCH_SIZE_HALF], &us, 8); } /* * Pack the data into the target buffer */ size = 1 + 8 + 8; if (!use_userdata) { if (is_inbound) { us_name = ((struct sockaddr_un *)sock_address)->sun_path; } else { bpf_getsockname(sock, peer_address, 1); us_name = ((struct sockaddr_un *)peer_address)->sun_path; } } else { /* * Map the user-provided address to a sockaddr_in */ struct sockaddr_un *usrsockaddr_un = (struct sockaddr_un *)usrsockaddr; /* * Put a 0 at the end of struct sockaddr_un because * the user might not have considered it in the length */ if (ulen == sizeof(struct sockaddr_storage)) ((char *)usrsockaddr_un)[(ulen - 1) & SCRATCH_SIZE_MAX] = 0; else ((char *)usrsockaddr_un)[ulen & SCRATCH_SIZE_MAX] = 0; if (is_inbound) us_name = ((struct sockaddr_un *)sock_address)->sun_path; else us_name = usrsockaddr_un->sun_path; } int res = bpf_probe_read_str(&data->buf[(data->state->tail_ctx.curoff + 1 + 8 + 8) & SCRATCH_SIZE_HALF], UNIX_PATH_MAX, us_name); size += res; break; } } return size; } static __always_inline int __bpf_val_to_ring(struct filler_data *data, unsigned long val, unsigned long val_len, enum ppm_param_type type, u8 dyn_idx, bool enforce_snaplen) { unsigned int len_dyn = 0; unsigned int len; if (data->state->tail_ctx.curoff > SCRATCH_SIZE_HALF) return PPM_FAILURE_BUFFER_FULL; if (dyn_idx != (u8)-1) { *((u8 *)&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]) = dyn_idx; len_dyn = sizeof(u8); data->state->tail_ctx.curoff += len_dyn; data->state->tail_ctx.len += len_dyn; } if (data->state->tail_ctx.curoff > SCRATCH_SIZE_HALF) return PPM_FAILURE_BUFFER_FULL; switch (type) { case PT_CHARBUF: case PT_FSPATH: { if (!data->curarg_already_on_frame) { int res; res = bpf_probe_read_str(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], PPM_MAX_ARG_SIZE, (const void *)val); if (res < 0) return PPM_FAILURE_INVALID_USER_MEMORY; len = res; } else { len = val_len; } break; } case PT_BYTEBUF: { if (val_len) { len = val_len; if (enforce_snaplen) { u32 dpi_lookahead_size = DPI_LOOKAHEAD_SIZE; unsigned int sl; if (dpi_lookahead_size > len) dpi_lookahead_size = len; if (!data->curarg_already_on_frame) { volatile unsigned long read_size = dpi_lookahead_size; #ifdef BPF_FORBIDS_ZERO_ACCESS if (read_size) if (bpf_probe_read(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], ((read_size - 1) & SCRATCH_SIZE_HALF) + 1, (void *)val)) #else if (bpf_probe_read(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], read_size & SCRATCH_SIZE_HALF, (void *)val)) #endif return PPM_FAILURE_INVALID_USER_MEMORY; } sl = bpf_compute_snaplen(data, dpi_lookahead_size); if (len > sl) len = sl; } if (len > PPM_MAX_ARG_SIZE) len = PPM_MAX_ARG_SIZE; if (!data->curarg_already_on_frame) { volatile unsigned long read_size = len; #ifdef BPF_FORBIDS_ZERO_ACCESS if (read_size) if (bpf_probe_read(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], ((read_size - 1) & SCRATCH_SIZE_HALF) + 1, (void *)val)) #else if (bpf_probe_read(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], read_size & SCRATCH_SIZE_HALF, (void *)val)) #endif return PPM_FAILURE_INVALID_USER_MEMORY; } } else { len = 0; } break; } case PT_SOCKADDR: case PT_SOCKTUPLE: case PT_FDLIST: if (!data->curarg_already_on_frame) { bpf_printk("expected arg already on frame: evt_type %d, curarg %d, type %d\n", data->state->tail_ctx.evt_type, data->state->tail_ctx.curarg, type); return PPM_FAILURE_BUG; } len = val_len; break; case PT_FLAGS8: case PT_UINT8: case PT_SIGTYPE: *((u8 *)&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]) = val; len = sizeof(u8); break; case PT_FLAGS16: case PT_UINT16: case PT_SYSCALLID: *((u16 *)&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]) = val; len = sizeof(u16); break; case PT_FLAGS32: case PT_MODE: case PT_UINT32: case PT_UID: case PT_GID: case PT_SIGSET: *((u32 *)&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]) = val; len = sizeof(u32); break; case PT_RELTIME: case PT_ABSTIME: case PT_UINT64: *((u64 *)&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]) = val; len = sizeof(u64); break; case PT_INT8: *((s8 *)&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]) = val; len = sizeof(s8); break; case PT_INT16: *((s16 *)&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]) = val; len = sizeof(s16); break; case PT_INT32: *((s32 *)&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]) = val; len = sizeof(s32); break; case PT_INT64: case PT_ERRNO: case PT_FD: case PT_PID: *((s64 *)&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]) = val; len = sizeof(s64); break; default: { bpf_printk("unhandled type in bpf_val_to_ring: evt_type %d, curarg %d, type %d\n", data->state->tail_ctx.evt_type, data->state->tail_ctx.curarg, type); return PPM_FAILURE_BUG; } } if (len_dyn + len > PPM_MAX_ARG_SIZE) return PPM_FAILURE_BUFFER_FULL; fixup_evt_arg_len(data->buf, data->state->tail_ctx.curarg, len_dyn + len); data->state->tail_ctx.curoff += len; data->state->tail_ctx.len += len; data->curarg_already_on_frame = false; ++data->state->tail_ctx.curarg; return PPM_SUCCESS; } static __always_inline int bpf_val_to_ring(struct filler_data *data, unsigned long val) { const struct ppm_param_info *param_info; if (data->state->tail_ctx.curarg >= PPM_MAX_EVENT_PARAMS) { bpf_printk("invalid curarg: %d\n", data->state->tail_ctx.curarg); return PPM_FAILURE_BUG; } param_info = &data->evt->params[data->state->tail_ctx.curarg & (PPM_MAX_EVENT_PARAMS - 1)]; return __bpf_val_to_ring(data, val, 0, param_info->type, -1, false); } static __always_inline int bpf_val_to_ring_len(struct filler_data *data, unsigned long val, unsigned long val_len) { const struct ppm_param_info *param_info; if (data->state->tail_ctx.curarg >= PPM_MAX_EVENT_PARAMS) { bpf_printk("invalid curarg: %d\n", data->state->tail_ctx.curarg); return PPM_FAILURE_BUG; } param_info = &data->evt->params[data->state->tail_ctx.curarg & (PPM_MAX_EVENT_PARAMS - 1)]; return __bpf_val_to_ring(data, val, val_len, param_info->type, -1, false); } static __always_inline int bpf_val_to_ring_dyn(struct filler_data *data, unsigned long val, enum ppm_param_type type, u8 dyn_idx) { return __bpf_val_to_ring(data, val, 0, type, dyn_idx, false); } static __always_inline int bpf_val_to_ring_type(struct filler_data *data, unsigned long val, enum ppm_param_type type) { return __bpf_val_to_ring(data, val, 0, type, -1, false); } static __always_inline bool bpf_in_ia32_syscall() { struct task_struct *task; u32 status; task = (struct task_struct *)bpf_get_current_task(); #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 18) status = _READ(task->thread.status); #elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) status = _READ(task->thread_info.status); #elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 2) status = _READ(task->thread.status); #else status = _READ(task->thread_info.status); #endif return status & TS_COMPAT; } #endif sysdig-0.26.4/driver/bpf/fillers.h000066400000000000000000002432461352731327100167770ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifndef __FILLERS_H #define __FILLERS_H /* * https://chromium.googlesource.com/chromiumos/third_party/kernel/+/096925a44076ba5c52faa84d255a847130ff341e%5E%21/#F2 * This commit diverged the ChromiumOS kernel from stock in the area of audit * information, which this probe accesses. * * If running on a patched version of COS, enable this #define to get the * probe to build. */ //#define COS_73_WORKAROUND #include "../ppm_flag_helpers.h" #include #include #define FILLER_RAW(x) \ static __always_inline int __bpf_##x(struct filler_data *data); \ \ __bpf_section(TP_NAME "filler/" #x) \ static __always_inline int bpf_##x(void *ctx) \ #define FILLER(x, is_syscall) \ static __always_inline int __bpf_##x(struct filler_data *data); \ \ __bpf_section(TP_NAME "filler/" #x) \ static __always_inline int bpf_##x(void *ctx) \ { \ struct filler_data data; \ int res; \ \ res = init_filler_data(ctx, &data, is_syscall); \ if (res == PPM_SUCCESS) { \ if (!data.state->tail_ctx.len) \ write_evt_hdr(&data); \ res = __bpf_##x(&data); \ } \ \ if (res == PPM_SUCCESS) \ res = push_evt_frame(ctx, &data); \ \ if (data.state) \ data.state->tail_ctx.prev_res = res; \ \ bpf_tail_call(ctx, &tail_map, PPM_FILLER_terminate_filler); \ bpf_printk("Can't tail call terminate filler\n"); \ return 0; \ } \ \ static __always_inline int __bpf_##x(struct filler_data *data) \ FILLER_RAW(terminate_filler) { struct sysdig_bpf_per_cpu_state *state; state = get_local_state(bpf_get_smp_processor_id()); if (!state) return 0; switch (state->tail_ctx.prev_res) { case PPM_SUCCESS: break; case PPM_FAILURE_BUFFER_FULL: bpf_printk("PPM_FAILURE_BUFFER_FULL event=%d curarg=%d\n", state->tail_ctx.evt_type, state->tail_ctx.curarg); ++state->n_drops_buffer; break; case PPM_FAILURE_INVALID_USER_MEMORY: bpf_printk("PPM_FAILURE_INVALID_USER_MEMORY event=%d curarg=%d\n", state->tail_ctx.evt_type, state->tail_ctx.curarg); ++state->n_drops_pf; break; case PPM_FAILURE_BUG: bpf_printk("PPM_FAILURE_BUG event=%d curarg=%d\n", state->tail_ctx.evt_type, state->tail_ctx.curarg); ++state->n_drops_bug; break; case PPM_SKIP_EVENT: break; default: bpf_printk("Unknown filler res=%d event=%d curarg=%d\n", state->tail_ctx.prev_res, state->tail_ctx.evt_type, state->tail_ctx.curarg); break; } release_local_state(state); return 0; } FILLER(sys_empty, true) { return PPM_SUCCESS; } FILLER(sys_single, true) { unsigned long val; int res; val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); return res; } FILLER(sys_single_x, true) { int res; long retval; retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); return res; } FILLER(sys_open_x, true) { unsigned int flags; unsigned int mode; unsigned long val; unsigned long dev; unsigned long ino; long retval; int res; /* * fd */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * Name */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * Flags */ val = bpf_syscall_get_argument(data, 1); flags = open_flags_to_scap(val); res = bpf_val_to_ring(data, flags); if (res != PPM_SUCCESS) return res; /* * Mode */ mode = bpf_syscall_get_argument(data, 2); mode = open_modes_to_scap(val, mode); res = bpf_val_to_ring(data, mode); if (res != PPM_SUCCESS) return res; /* * Device */ if (retval < 0 || !bpf_get_fd_dev_ino(retval, &dev, &ino)) dev = 0; res = bpf_val_to_ring(data, dev); return res; } FILLER(sys_read_x, true) { unsigned long bufsize; unsigned long val; long retval; int res; /* * res */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; if (retval < 0) { val = 0; bufsize = 0; } else { val = bpf_syscall_get_argument(data, 1); bufsize = retval; } /* * data */ data->fd = bpf_syscall_get_argument(data, 0); res = __bpf_val_to_ring(data, val, bufsize, PT_BYTEBUF, -1, true); return res; } FILLER(sys_write_x, true) { unsigned long bufsize; unsigned long val; long retval; int res; /* * res */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * data */ data->fd = bpf_syscall_get_argument(data, 0); val = bpf_syscall_get_argument(data, 1); bufsize = bpf_syscall_get_argument(data, 2); res = __bpf_val_to_ring(data, val, bufsize, PT_BYTEBUF, -1, true); return res; } #define POLL_MAXFDS 16 static __always_inline int bpf_poll_parse_fds(struct filler_data *data, bool enter_event) { unsigned int read_size; unsigned int fds_count; int res = PPM_SUCCESS; unsigned long nfds; struct pollfd *fds; unsigned long val; unsigned long off; int j; nfds = bpf_syscall_get_argument(data, 1); fds = (struct pollfd *)data->tmp_scratch; read_size = nfds * sizeof(struct pollfd); if (read_size > SCRATCH_SIZE_MAX) return PPM_FAILURE_BUFFER_FULL; val = bpf_syscall_get_argument(data, 0); #ifdef BPF_FORBIDS_ZERO_ACCESS if (read_size) if (bpf_probe_read(fds, ((read_size - 1) & SCRATCH_SIZE_MAX) + 1, (void *)val)) #else if (bpf_probe_read(fds, read_size & SCRATCH_SIZE_MAX, (void *)val)) #endif return PPM_FAILURE_INVALID_USER_MEMORY; off = data->state->tail_ctx.curoff + sizeof(u16); fds_count = 0; #pragma unroll for (j = 0; j < POLL_MAXFDS; ++j) { u16 flags; if (j == nfds) break; if (enter_event) { flags = poll_events_to_scap(fds[j].events); } else { if (!fds[j].revents) continue; flags = poll_events_to_scap(fds[j].revents); } if (off > SCRATCH_SIZE_HALF) return PPM_FAILURE_BUFFER_FULL; *(s64 *)&data->buf[off & SCRATCH_SIZE_HALF] = fds[j].fd; off += sizeof(s64); if (off > SCRATCH_SIZE_HALF) return PPM_FAILURE_BUFFER_FULL; *(s16 *)&data->buf[off & SCRATCH_SIZE_HALF] = flags; off += sizeof(s16); ++fds_count; } *((u16 *)&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]) = fds_count; data->curarg_already_on_frame = true; return __bpf_val_to_ring(data, 0, off - data->state->tail_ctx.curoff, PT_FDLIST, -1, false); } FILLER(sys_poll_e, true) { unsigned long val; int res; /* * fds */ res = bpf_poll_parse_fds(data, true); if (res != PPM_SUCCESS) return res; /* * timeout */ val = bpf_syscall_get_argument(data, 2); res = bpf_val_to_ring(data, val); return res; } FILLER(sys_poll_x, true) { long retval; int res; /* * res */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; /* * fds */ res = bpf_poll_parse_fds(data, false); return res; } #define MAX_IOVCNT 32 static __always_inline int bpf_parse_readv_writev_bufs(struct filler_data *data, const struct iovec __user *iovsrc, unsigned long iovcnt, long retval, int flags) { const struct iovec *iov; int res = PPM_SUCCESS; unsigned int copylen; long size = 0; int j; copylen = iovcnt * sizeof(struct iovec); iov = (const struct iovec *)data->tmp_scratch; if (copylen > SCRATCH_SIZE_MAX) return PPM_FAILURE_BUFFER_FULL; #ifdef BPF_FORBIDS_ZERO_ACCESS if (copylen) if (bpf_probe_read((void *)iov, ((copylen - 1) & SCRATCH_SIZE_MAX) + 1, (void *)iovsrc)) #else if (bpf_probe_read((void *)iov, copylen & SCRATCH_SIZE_MAX, (void *)iovsrc)) #endif return PPM_FAILURE_INVALID_USER_MEMORY; #pragma unroll for (j = 0; j < MAX_IOVCNT; ++j) { if (j == iovcnt) break; size += iov[j].iov_len; } if ((flags & PRB_FLAG_IS_WRITE) == 0) if (size > retval) size = retval; if (flags & PRB_FLAG_PUSH_SIZE) { res = bpf_val_to_ring_type(data, size, PT_UINT32); if (res != PPM_SUCCESS) return res; } if (flags & PRB_FLAG_PUSH_DATA) { if (size > 0) { unsigned long off = data->state->tail_ctx.curoff; unsigned long remaining = size; int j; #pragma unroll for (j = 0; j < MAX_IOVCNT; ++j) { volatile unsigned int to_read; if (j == iovcnt) break; if (off > SCRATCH_SIZE_HALF) break; if (iov[j].iov_len <= remaining) to_read = iov[j].iov_len; else to_read = remaining; if (to_read > SCRATCH_SIZE_HALF) to_read = SCRATCH_SIZE_HALF; #ifdef BPF_FORBIDS_ZERO_ACCESS if (to_read) if (bpf_probe_read(&data->buf[off & SCRATCH_SIZE_HALF], ((to_read - 1) & SCRATCH_SIZE_HALF) + 1, iov[j].iov_base)) #else if (bpf_probe_read(&data->buf[off & SCRATCH_SIZE_HALF], to_read & SCRATCH_SIZE_HALF, iov[j].iov_base)) #endif return PPM_FAILURE_INVALID_USER_MEMORY; remaining -= to_read; off += to_read; } } else { size = 0; } data->fd = bpf_syscall_get_argument(data, 0); data->curarg_already_on_frame = true; return __bpf_val_to_ring(data, 0, size, PT_BYTEBUF, -1, true); } return res; } FILLER(sys_readv_preadv_x, true) { const struct iovec __user *iov; unsigned long iovcnt; long retval; int res; /* * res */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; iov = (const struct iovec __user *)bpf_syscall_get_argument(data, 1); iovcnt = bpf_syscall_get_argument(data, 2); res = bpf_parse_readv_writev_bufs(data, iov, iovcnt, retval, PRB_FLAG_PUSH_ALL); return res; } FILLER(sys_writev_e, true) { unsigned long iovcnt; unsigned long val; int res; /* * fd */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; val = bpf_syscall_get_argument(data, 1); iovcnt = bpf_syscall_get_argument(data, 2); res = bpf_parse_readv_writev_bufs(data, (const struct iovec __user *)val, iovcnt, 0, PRB_FLAG_PUSH_SIZE | PRB_FLAG_IS_WRITE); return res; } FILLER(sys_writev_pwritev_x, true) { unsigned long iovcnt; unsigned long val; long retval; int res; /* * res */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * data and size */ val = bpf_syscall_get_argument(data, 1); iovcnt = bpf_syscall_get_argument(data, 2); res = bpf_parse_readv_writev_bufs(data, (const struct iovec __user *)val, iovcnt, 0, PRB_FLAG_PUSH_DATA | PRB_FLAG_IS_WRITE); return res; } static __always_inline int timespec_parse(struct filler_data *data, unsigned long val) { u64 longtime; struct timespec ts; if (bpf_probe_read(&ts, sizeof(ts), (void *)val)) return PPM_FAILURE_INVALID_USER_MEMORY; longtime = ((u64)ts.tv_sec) * 1000000000 + ts.tv_nsec; return bpf_val_to_ring_type(data, longtime, PT_RELTIME); } FILLER(sys_nanosleep_e, true) { unsigned long val; int res; val = bpf_syscall_get_argument(data, 0); res = timespec_parse(data, val); return res; } FILLER(sys_futex_e, true) { unsigned long val; int res; /* * addr */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * op */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, futex_op_to_scap(val)); if (res != PPM_SUCCESS) return res; /* * val */ val = bpf_syscall_get_argument(data, 2); res = bpf_val_to_ring(data, val); return res; } static __always_inline unsigned long bpf_get_mm_counter(struct mm_struct *mm, int member) { long val; bpf_probe_read(&val, sizeof(val), &mm->rss_stat.count[member]); if (val < 0) val = 0; return (unsigned long)val; } static __always_inline unsigned long bpf_get_mm_rss(struct mm_struct *mm) { return bpf_get_mm_counter(mm, MM_FILEPAGES) + bpf_get_mm_counter(mm, MM_ANONPAGES) + bpf_get_mm_counter(mm, MM_SHMEMPAGES); } static __always_inline unsigned long bpf_get_mm_swap(struct mm_struct *mm) { return bpf_get_mm_counter(mm, MM_SWAPENTS); } FILLER(sys_brk_munmap_mmap_x, true) { struct task_struct *task; unsigned long total_vm = 0; struct mm_struct *mm; long total_rss = 0; long swap = 0; long retval; int res; task = (struct task_struct *)bpf_get_current_task(); mm = NULL; bpf_probe_read(&mm, sizeof(mm), &task->mm); retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring_type(data, retval, PT_UINT64); if (res != PPM_SUCCESS) return res; if (mm) { total_vm = _READ(mm->total_vm); total_vm <<= (PAGE_SHIFT - 10); total_rss = bpf_get_mm_rss(mm) << (PAGE_SHIFT - 10); swap = bpf_get_mm_swap(mm) << (PAGE_SHIFT - 10); } /* * vm_size */ res = bpf_val_to_ring_type(data, total_vm, PT_UINT32); if (res != PPM_SUCCESS) return res; /* * vm_rss */ res = bpf_val_to_ring_type(data, total_rss, PT_UINT32); if (res != PPM_SUCCESS) return res; /* * vm_swap */ res = bpf_val_to_ring_type(data, swap, PT_UINT32); return res; } FILLER(sys_mmap_e, true) { unsigned long val; int res; /* * addr */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * length */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * prot */ val = bpf_syscall_get_argument(data, 2); res = bpf_val_to_ring(data, prot_flags_to_scap(val)); if (res != PPM_SUCCESS) return res; /* * flags */ val = bpf_syscall_get_argument(data, 3); res = bpf_val_to_ring(data, mmap_flags_to_scap(val)); if (res != PPM_SUCCESS) return res; /* * fd */ val = bpf_syscall_get_argument(data, 4); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * offset/pgoffset */ val = bpf_syscall_get_argument(data, 5); res = bpf_val_to_ring(data, val); return res; } FILLER(sys_fcntl_e, true) { unsigned long val; long cmd; int res; /* * fd */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring_type(data, val, PT_FD); if (res != PPM_SUCCESS) return res; /* * cmd */ val = bpf_syscall_get_argument(data, 1); cmd = fcntl_cmd_to_scap(val); res = bpf_val_to_ring_type(data, cmd, PT_FLAGS8); return res; } FILLER(sys_access_e, true) { unsigned long val; int res; /* * mode */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, access_flags_to_scap(val)); return res; } FILLER(sys_getrlimit_setrlimit_e, true) { unsigned long val; int res; /* * resource */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring_type(data, rlimit_resource_to_scap(val), PT_FLAGS8); return res; } FILLER(sys_getrlimit_setrlrimit_x, true) { unsigned long val; long retval; s64 cur; s64 max; int res; /* * res */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * Copy the user structure and extract cur and max */ if (retval >= 0 || data->state->tail_ctx.evt_type == PPME_SYSCALL_SETRLIMIT_X) { struct rlimit rl; val = bpf_syscall_get_argument(data, 1); if (bpf_probe_read(&rl, sizeof(rl), (void *)val)) return PPM_FAILURE_INVALID_USER_MEMORY; cur = rl.rlim_cur; max = rl.rlim_max; } else { cur = -1; max = -1; } /* * cur */ res = bpf_val_to_ring(data, cur); if (res != PPM_SUCCESS) return res; /* * max */ res = bpf_val_to_ring(data, max); return res; } FILLER(sys_connect_x, true) { struct sockaddr *usrsockaddr; unsigned long val; long size = 0; long retval; int err; int res; int fd; /* * Push the result */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * Retrieve the fd and push it to the ring. * Note that, even if we are in the exit callback, the arguments are still * in the stack, and therefore we can consume them. */ fd = bpf_syscall_get_argument(data, 0); if (fd >= 0) { usrsockaddr = (struct sockaddr *)bpf_syscall_get_argument(data, 1); val = bpf_syscall_get_argument(data, 2); if (usrsockaddr && val != 0) { /* * Copy the address */ err = bpf_addr_to_kernel(usrsockaddr, val, (struct sockaddr *)data->tmp_scratch); if (err >= 0) { /* * Convert the fd into socket endpoint information */ size = bpf_fd_to_socktuple(data, fd, (struct sockaddr *)data->tmp_scratch, val, true, false, data->tmp_scratch + sizeof(struct sockaddr_storage)); } } } /* * Copy the endpoint info into the ring */ data->curarg_already_on_frame = true; res = bpf_val_to_ring_len(data, 0, size); return res; } FILLER(sys_socketpair_x, true) { struct unix_sock *us = NULL; struct sock *speer = NULL; int fds[2] = { 0 }; unsigned long val; long retval; int res; /* ret */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; if (retval >= 0) { val = bpf_syscall_get_argument(data, 3); if (bpf_probe_read(fds, 2 * sizeof(int), (void *)val)) return PPM_FAILURE_INVALID_USER_MEMORY; struct socket *sock = bpf_sockfd_lookup(data, fds[0]); if (sock) { us = (struct unix_sock *)_READ(sock->sk); speer = _READ(us->peer); } } /* fd1 */ res = bpf_val_to_ring_type(data, fds[0], PT_FD); if (res != PPM_SUCCESS) return res; /* fd2 */ res = bpf_val_to_ring_type(data, fds[1], PT_FD); if (res != PPM_SUCCESS) return res; /* source */ res = bpf_val_to_ring_type(data, (unsigned long)us, PT_UINT64); if (res != PPM_SUCCESS) return res; /* peer */ res = bpf_val_to_ring_type(data, (unsigned long)speer, PT_UINT64); return res; } static int __always_inline parse_sockopt(struct filler_data *data, int level, int optname, void *optval, int optlen) { union { uint32_t val32; uint64_t val64; struct timeval tv; } u; if (level == SOL_SOCKET) { switch (optname) { #ifdef SO_ERROR case SO_ERROR: if (bpf_probe_read(&u.val32, sizeof(u.val32), optval)) return PPM_FAILURE_INVALID_USER_MEMORY; return bpf_val_to_ring_dyn(data, -u.val32, PT_ERRNO, PPM_SOCKOPT_IDX_ERRNO); #endif #ifdef SO_RCVTIMEO case SO_RCVTIMEO: #endif #ifdef SO_SNDTIMEO case SO_SNDTIMEO: #endif if (bpf_probe_read(&u.tv, sizeof(u.tv), optval)) return PPM_FAILURE_INVALID_USER_MEMORY; return bpf_val_to_ring_dyn(data, u.tv.tv_sec * 1000000000 + u.tv.tv_usec * 1000, PT_RELTIME, PPM_SOCKOPT_IDX_TIMEVAL); #ifdef SO_COOKIE case SO_COOKIE: if (bpf_probe_read(&u.val64, sizeof(u.val64), optval)) return PPM_FAILURE_INVALID_USER_MEMORY; return bpf_val_to_ring_dyn(data, u.val64, PT_UINT64, PPM_SOCKOPT_IDX_UINT64); #endif #ifdef SO_DEBUG case SO_DEBUG: #endif #ifdef SO_REUSEADDR case SO_REUSEADDR: #endif #ifdef SO_TYPE case SO_TYPE: #endif #ifdef SO_DONTROUTE case SO_DONTROUTE: #endif #ifdef SO_BROADCAST case SO_BROADCAST: #endif #ifdef SO_SNDBUF case SO_SNDBUF: #endif #ifdef SO_RCVBUF case SO_RCVBUF: #endif #ifdef SO_SNDBUFFORCE case SO_SNDBUFFORCE: #endif #ifdef SO_RCVBUFFORCE case SO_RCVBUFFORCE: #endif #ifdef SO_KEEPALIVE case SO_KEEPALIVE: #endif #ifdef SO_OOBINLINE case SO_OOBINLINE: #endif #ifdef SO_NO_CHECK case SO_NO_CHECK: #endif #ifdef SO_PRIORITY case SO_PRIORITY: #endif #ifdef SO_BSDCOMPAT case SO_BSDCOMPAT: #endif #ifdef SO_REUSEPORT case SO_REUSEPORT: #endif #ifdef SO_PASSCRED case SO_PASSCRED: #endif #ifdef SO_RCVLOWAT case SO_RCVLOWAT: #endif #ifdef SO_SNDLOWAT case SO_SNDLOWAT: #endif #ifdef SO_SECURITY_AUTHENTICATION case SO_SECURITY_AUTHENTICATION: #endif #ifdef SO_SECURITY_ENCRYPTION_TRANSPORT case SO_SECURITY_ENCRYPTION_TRANSPORT: #endif #ifdef SO_SECURITY_ENCRYPTION_NETWORK case SO_SECURITY_ENCRYPTION_NETWORK: #endif #ifdef SO_BINDTODEVICE case SO_BINDTODEVICE: #endif #ifdef SO_DETACH_FILTER case SO_DETACH_FILTER: #endif #ifdef SO_TIMESTAMP case SO_TIMESTAMP: #endif #ifdef SO_ACCEPTCONN case SO_ACCEPTCONN: #endif #ifdef SO_PEERSEC case SO_PEERSEC: #endif #ifdef SO_PASSSEC case SO_PASSSEC: #endif #ifdef SO_TIMESTAMPNS case SO_TIMESTAMPNS: #endif #ifdef SO_MARK case SO_MARK: #endif #ifdef SO_TIMESTAMPING case SO_TIMESTAMPING: #endif #ifdef SO_PROTOCOL case SO_PROTOCOL: #endif #ifdef SO_DOMAIN case SO_DOMAIN: #endif #ifdef SO_RXQ_OVFL case SO_RXQ_OVFL: #endif #ifdef SO_WIFI_STATUS case SO_WIFI_STATUS: #endif #ifdef SO_PEEK_OFF case SO_PEEK_OFF: #endif #ifdef SO_NOFCS case SO_NOFCS: #endif #ifdef SO_LOCK_FILTER case SO_LOCK_FILTER: #endif #ifdef SO_SELECT_ERR_QUEUE case SO_SELECT_ERR_QUEUE: #endif #ifdef SO_BUSY_POLL case SO_BUSY_POLL: #endif #ifdef SO_MAX_PACING_RATE case SO_MAX_PACING_RATE: #endif #ifdef SO_BPF_EXTENSIONS case SO_BPF_EXTENSIONS: #endif #ifdef SO_INCOMING_CPU case SO_INCOMING_CPU: #endif if (bpf_probe_read(&u.val32, sizeof(u.val32), optval)) return PPM_FAILURE_INVALID_USER_MEMORY; return bpf_val_to_ring_dyn(data, u.val32, PT_UINT32, PPM_SOCKOPT_IDX_UINT32); default: return __bpf_val_to_ring(data, (unsigned long)optval, optlen, PT_BYTEBUF, PPM_SOCKOPT_IDX_UNKNOWN, false); } } else { return __bpf_val_to_ring(data, (unsigned long)optval, optlen, PT_BYTEBUF, PPM_SOCKOPT_IDX_UNKNOWN, false); } } FILLER(sys_setsockopt_x, true) { int res; unsigned long retval, fd, level, optname, optval, optlen; retval = bpf_syscall_get_retval(data->ctx); /* retval */ res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; /* fd */ fd = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring_type(data, fd, PT_FD); if (res != PPM_SUCCESS) return res; /* level */ level = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring_type(data, sockopt_level_to_scap(level), PT_FLAGS8); if (res != PPM_SUCCESS) return res; /* optname */ optname = bpf_syscall_get_argument(data, 2); res = bpf_val_to_ring_type(data, sockopt_optname_to_scap(level, optname), PT_FLAGS8); if (res != PPM_SUCCESS) return res; /* optval */ optval = bpf_syscall_get_argument(data, 3); optlen = bpf_syscall_get_argument(data, 4); res = parse_sockopt(data, level, optname, (void*)optval, optlen); if (res != PPM_SUCCESS) return res; /* optlen */ res = bpf_val_to_ring_type(data, optlen, PT_UINT32); return res; } FILLER(sys_getsockopt_x, true) { int res; unsigned long retval, fd, level, optname, optval, optlen_p, optlen; retval = bpf_syscall_get_retval(data->ctx); /* retval */ res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; /* fd */ fd = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring_type(data, fd, PT_FD); if (res != PPM_SUCCESS) return res; /* level */ level = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring_type(data, sockopt_level_to_scap(level), PT_FLAGS8); if (res != PPM_SUCCESS) return res; /* optname */ optname = bpf_syscall_get_argument(data, 2); res = bpf_val_to_ring_type(data, sockopt_optname_to_scap(level, optname), PT_FLAGS8); if (res != PPM_SUCCESS) return res; /* optval */ optval = bpf_syscall_get_argument(data, 3); optlen_p = bpf_syscall_get_argument(data, 4); if (bpf_probe_read(&optlen, sizeof(optlen), (void*)optlen_p)) return PPM_FAILURE_INVALID_USER_MEMORY; res = parse_sockopt(data, level, optname, (void*)optval, optlen); if (res != PPM_SUCCESS) return res; /* optlen */ res = bpf_val_to_ring_type(data, optlen, PT_UINT32); return res; } static __always_inline int f_sys_send_e_common(struct filler_data *data, int fd) { unsigned long val; int res; /* * fd */ res = bpf_val_to_ring(data, fd); if (res != PPM_SUCCESS) return res; /* * size */ val = bpf_syscall_get_argument(data, 2); res = bpf_val_to_ring(data, val); return res; } FILLER(sys_send_e, true) { int res; int fd; /* * Push the common params to the ring */ fd = bpf_syscall_get_argument(data, 0); res = f_sys_send_e_common(data, fd); return res; } FILLER(sys_sendto_e, true) { struct sockaddr __user *usrsockaddr; unsigned long val; long size = 0; int err = 0; int res; int fd; /* * Push the common params to the ring */ fd = bpf_syscall_get_argument(data, 0); res = f_sys_send_e_common(data, fd); if (res != PPM_SUCCESS) return res; /* * Get the address */ val = bpf_syscall_get_argument(data, 4); usrsockaddr = (struct sockaddr __user *)val; /* * Get the address len */ val = bpf_syscall_get_argument(data, 5); if (usrsockaddr && val != 0) { /* * Copy the address */ err = bpf_addr_to_kernel(usrsockaddr, val, (struct sockaddr *)data->tmp_scratch); if (err >= 0) { /* * Convert the fd into socket endpoint information */ size = bpf_fd_to_socktuple(data, fd, (struct sockaddr *)data->tmp_scratch, val, true, false, data->tmp_scratch + sizeof(struct sockaddr_storage)); } } /* * Copy the endpoint info into the ring */ data->curarg_already_on_frame = true; res = bpf_val_to_ring_len(data, 0, size); return res; } FILLER(sys_send_x, true) { unsigned long bufsize; unsigned long val; long retval; int res; /* * res */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; /* * data */ if (retval < 0) { /* * The operation failed, return an empty buffer */ val = 0; bufsize = 0; } else { val = bpf_syscall_get_argument(data, 1); /* * The return value can be lower than the value provided by the user, * and we take that into account. */ bufsize = retval; } data->fd = bpf_syscall_get_argument(data, 0); res = __bpf_val_to_ring(data, val, bufsize, PT_BYTEBUF, -1, true); return res; } FILLER(sys_execve_e, true) { unsigned long val; int res; /* * filename */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res == PPM_FAILURE_INVALID_USER_MEMORY) { char na[] = ""; res = bpf_val_to_ring(data, (unsigned long)na); } return res; } static __always_inline int bpf_ppm_get_tty(struct task_struct *task) { struct signal_struct *sig; struct tty_struct *tty; struct tty_driver *driver; int major; int minor_start; int index; int tty_nr = 0; sig = _READ(task->signal); if (!sig) return 0; tty = _READ(sig->tty); if (!tty) return 0; index = _READ(tty->index); driver = _READ(tty->driver); if (!driver) return 0; major = _READ(driver->major); minor_start = _READ(driver->minor_start); tty_nr = new_encode_dev(MKDEV(major, minor_start) + index); return tty_nr; } static __always_inline struct pid *bpf_task_pid(struct task_struct *task) { #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) return _READ(task->pids[PIDTYPE_PID].pid); #else return _READ(task->thread_pid); #endif } static __always_inline struct pid_namespace *bpf_ns_of_pid(struct pid *pid) { struct pid_namespace *ns = NULL; if (pid) ns = _READ(pid->numbers[_READ(pid->level)].ns); return ns; } static __always_inline struct pid_namespace *bpf_task_active_pid_ns(struct task_struct *tsk) { return bpf_ns_of_pid(bpf_task_pid(tsk)); } static __always_inline pid_t bpf_pid_nr_ns(struct pid *pid, struct pid_namespace *ns) { unsigned int ns_level; struct upid *upid; pid_t nr = 0; ns_level = _READ(ns->level); if (pid && ns_level <= _READ(pid->level)) { upid = &pid->numbers[ns_level]; if (_READ(upid->ns) == ns) nr = _READ(upid->nr); } return nr; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) static __always_inline struct pid **bpf_task_pid_ptr(struct task_struct *task, enum pid_type type) { return (type == PIDTYPE_PID) ? &task->thread_pid : &_READ(task->signal)->pids[type]; } #endif static __always_inline pid_t bpf_task_pid_nr_ns(struct task_struct *task, enum pid_type type, struct pid_namespace *ns) { pid_t nr = 0; if (!ns) ns = bpf_task_active_pid_ns(task); #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) if (type != PIDTYPE_PID) { if (type == __PIDTYPE_TGID) type = PIDTYPE_PID; task = _READ(task->group_leader); } nr = bpf_pid_nr_ns(_READ(task->pids[type].pid), ns); #else nr = bpf_pid_nr_ns(_READ(*bpf_task_pid_ptr(task, type)), ns); #endif return nr; } static __always_inline pid_t bpf_task_pid_vnr(struct task_struct *task) { return bpf_task_pid_nr_ns(task, PIDTYPE_PID, NULL); } static __always_inline pid_t bpf_task_tgid_vnr(struct task_struct *task) { #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) return bpf_task_pid_nr_ns(task, __PIDTYPE_TGID, NULL); #else return bpf_task_pid_nr_ns(task, PIDTYPE_TGID, NULL); #endif } static __always_inline pid_t bpf_task_pgrp_vnr(struct task_struct *task) { return bpf_task_pid_nr_ns(task, PIDTYPE_PGID, NULL); } #define MAX_CGROUP_PATHS 6 static __always_inline int __bpf_append_cgroup(struct css_set *cgroups, int subsys_id, char *buf, int *len) { struct cgroup_subsys_state *css = _READ(cgroups->subsys[subsys_id]); struct cgroup_subsys *ss = _READ(css->ss); char *subsys_name = (char *)_READ(ss->name); struct cgroup *cgroup = _READ(css->cgroup); struct kernfs_node *kn = _READ(cgroup->kn); char *cgroup_path[MAX_CGROUP_PATHS]; bool prev_empty = false; int off = *len; if (off > SCRATCH_SIZE_HALF) return PPM_FAILURE_BUFFER_FULL; int res = bpf_probe_read_str(&buf[off & SCRATCH_SIZE_HALF], SCRATCH_SIZE_HALF, subsys_name); if (res < 0) return PPM_FAILURE_INVALID_USER_MEMORY; off += res - 1; if (off > SCRATCH_SIZE_HALF) return PPM_FAILURE_BUFFER_FULL; buf[off & SCRATCH_SIZE_HALF] = '='; ++off; #pragma unroll MAX_CGROUP_PATHS for (int k = 0; k < MAX_CGROUP_PATHS; ++k) { if (kn) { cgroup_path[k] = (char *)_READ(kn->name); kn = _READ(kn->parent); } else { cgroup_path[k] = NULL; } } #pragma unroll MAX_CGROUP_PATHS for (int k = MAX_CGROUP_PATHS - 1; k >= 0 ; --k) { if (cgroup_path[k]) { if (!prev_empty) { if (off > SCRATCH_SIZE_HALF) return PPM_FAILURE_BUFFER_FULL; buf[off & SCRATCH_SIZE_HALF] = '/'; ++off; } prev_empty = false; if (off > SCRATCH_SIZE_HALF) return PPM_FAILURE_BUFFER_FULL; res = bpf_probe_read_str(&buf[off & SCRATCH_SIZE_HALF], SCRATCH_SIZE_HALF, cgroup_path[k]); if (res > 1) off += res - 1; else if (res == 1) prev_empty = true; else return PPM_FAILURE_INVALID_USER_MEMORY; } } if (off > SCRATCH_SIZE_HALF) return PPM_FAILURE_BUFFER_FULL; buf[off & SCRATCH_SIZE_HALF] = 0; ++off; *len = off; return PPM_SUCCESS; } static __always_inline int bpf_append_cgroup(struct task_struct *task, char *buf, int *len) { struct css_set *cgroups = _READ(task->cgroups); int res; #if IS_ENABLED(CONFIG_CPUSETS) res = __bpf_append_cgroup(cgroups, cpuset_cgrp_id, buf, len); if (res != PPM_SUCCESS) return res; #endif #if IS_ENABLED(CONFIG_CGROUP_SCHED) res = __bpf_append_cgroup(cgroups, cpu_cgrp_id, buf, len); if (res != PPM_SUCCESS) return res; #endif #if IS_ENABLED(CONFIG_CGROUP_CPUACCT) res = __bpf_append_cgroup(cgroups, cpuacct_cgrp_id, buf, len); if (res != PPM_SUCCESS) return res; #endif #if IS_ENABLED(CONFIG_BLK_CGROUP) res = __bpf_append_cgroup(cgroups, io_cgrp_id, buf, len); if (res != PPM_SUCCESS) return res; #endif #if IS_ENABLED(CONFIG_MEMCG) res = __bpf_append_cgroup(cgroups, memory_cgrp_id, buf, len); if (res != PPM_SUCCESS) return res; #endif return PPM_SUCCESS; } #define ARGS_ENV_SIZE_MAX 4096 #define FAILED_ARGS_ENV_ITEMS_MAX 16 static __always_inline int bpf_accumulate_argv_or_env(struct filler_data *data, char **argv, long *args_len) { char *arg; int off; int len; int j; *args_len = 0; off = data->state->tail_ctx.curoff; #pragma unroll for (j = 0; j < FAILED_ARGS_ENV_ITEMS_MAX; ++j) { arg = _READ(argv[j]); if (!arg) break; if (off > SCRATCH_SIZE_HALF) return PPM_FAILURE_BUFFER_FULL; len = bpf_probe_read_str(&data->buf[off & SCRATCH_SIZE_HALF], SCRATCH_SIZE_HALF, arg); if (len < 0) return PPM_FAILURE_INVALID_USER_MEMORY; *args_len += len; off += len; if (*args_len > ARGS_ENV_SIZE_MAX) { *args_len = ARGS_ENV_SIZE_MAX; data->buf[(data->state->tail_ctx.curoff + *args_len - 1) & SCRATCH_SIZE_MAX] = 0; break; } } return PPM_SUCCESS; } FILLER(proc_startupdate, true) { struct task_struct *real_parent; struct signal_struct *signal; struct task_struct *task; unsigned long total_vm; unsigned long min_flt; unsigned long maj_flt; unsigned long fdlimit; struct mm_struct *mm; long total_rss; char empty = 0; long args_len; long retval; pid_t tgid; long swap; pid_t pid; int res; /* * Make sure the operation was successful */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; task = (struct task_struct *)bpf_get_current_task(); mm = _READ(task->mm); if (!mm) return PPM_FAILURE_BUG; if (retval >= 0) { /* * The call succeeded. Get exe, args from the current * process; put one \0-separated exe-args string into * str_storage */ unsigned long arg_start; unsigned long arg_end; arg_end = _READ(mm->arg_end); if (!arg_end) return PPM_FAILURE_BUG; arg_start = _READ(mm->arg_start); args_len = arg_end - arg_start; if (args_len) { if (args_len > ARGS_ENV_SIZE_MAX) args_len = ARGS_ENV_SIZE_MAX; #ifdef BPF_FORBIDS_ZERO_ACCESS if (bpf_probe_read(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], ((args_len - 1) & SCRATCH_SIZE_HALF) + 1, (void *)arg_start)) #else if (bpf_probe_read(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], args_len & SCRATCH_SIZE_HALF, (void *)arg_start)) #endif args_len = 0; else data->buf[(data->state->tail_ctx.curoff + args_len - 1) & SCRATCH_SIZE_MAX] = 0; } } else if (data->state->tail_ctx.evt_type == PPME_SYSCALL_EXECVE_19_X) { unsigned long val; char **argv; val = bpf_syscall_get_argument(data, 1); argv = (char **)val; res = bpf_accumulate_argv_or_env(data, argv, &args_len); if (res != PPM_SUCCESS) args_len = 0; } else { args_len = 0; } if (args_len) { int exe_len; exe_len = bpf_probe_read_str(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], SCRATCH_SIZE_HALF, &data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF]); if (exe_len < 0) return PPM_FAILURE_INVALID_USER_MEMORY; /* * exe */ data->curarg_already_on_frame = true; res = __bpf_val_to_ring(data, 0, exe_len, PT_CHARBUF, -1, false); if (res != PPM_SUCCESS) return res; /* * Args */ data->curarg_already_on_frame = true; res = __bpf_val_to_ring(data, 0, args_len - exe_len, PT_BYTEBUF, -1, false); if (res != PPM_SUCCESS) return res; } else { /* * exe */ res = bpf_val_to_ring_type(data, (unsigned long)&empty, PT_CHARBUF); if (res != PPM_SUCCESS) return res; /* * Args */ res = bpf_val_to_ring_type(data, 0, PT_BYTEBUF); if (res != PPM_SUCCESS) return res; } /* * tid */ pid = _READ(task->pid); res = bpf_val_to_ring_type(data, pid, PT_PID); if (res != PPM_SUCCESS) return res; /* * pid */ tgid = _READ(task->tgid); res = bpf_val_to_ring_type(data, tgid, PT_PID); if (res != PPM_SUCCESS) return res; /* * ptid */ real_parent = _READ(task->real_parent); pid_t ptid = _READ(real_parent->pid); res = bpf_val_to_ring_type(data, ptid, PT_PID); if (res != PPM_SUCCESS) return res; /* * cwd, pushed empty to avoid breaking compatibility * with the older event format */ res = bpf_val_to_ring_type(data, (unsigned long)&empty, PT_CHARBUF); if (res != PPM_SUCCESS) return res; /* * fdlimit */ signal = _READ(task->signal); fdlimit = _READ(signal->rlim[RLIMIT_NOFILE].rlim_cur); res = bpf_val_to_ring_type(data, fdlimit, PT_UINT64); if (res != PPM_SUCCESS) return res; /* * pgft_maj */ maj_flt = _READ(task->maj_flt); res = bpf_val_to_ring_type(data, maj_flt, PT_UINT64); if (res != PPM_SUCCESS) return res; /* * pgft_min */ min_flt = _READ(task->min_flt); res = bpf_val_to_ring_type(data, min_flt, PT_UINT64); if (res != PPM_SUCCESS) return res; total_vm = 0; total_rss = 0; swap = 0; if (mm) { total_vm = _READ(mm->total_vm); total_vm <<= (PAGE_SHIFT - 10); total_rss = bpf_get_mm_rss(mm) << (PAGE_SHIFT - 10); swap = bpf_get_mm_swap(mm) << (PAGE_SHIFT - 10); } /* * vm_size */ res = bpf_val_to_ring_type(data, total_vm, PT_UINT32); if (res != PPM_SUCCESS) return res; /* * vm_rss */ res = bpf_val_to_ring_type(data, total_rss, PT_UINT32); if (res != PPM_SUCCESS) return res; /* * vm_swap */ res = bpf_val_to_ring_type(data, swap, PT_UINT32); if (res != PPM_SUCCESS) return res; /* * comm */ res = bpf_val_to_ring_type(data, (unsigned long)task->comm, PT_CHARBUF); if (res != PPM_SUCCESS) return res; bpf_tail_call(data->ctx, &tail_map, PPM_FILLER_proc_startupdate_2); bpf_printk("Can't tail call f_proc_startupdate_2 filler\n"); return PPM_FAILURE_BUG; } FILLER(proc_startupdate_2, true) { struct task_struct *task; int cgroups_len = 0; int res; task = (struct task_struct *)bpf_get_current_task(); /* * cgroups */ res = bpf_append_cgroup(task, data->tmp_scratch, &cgroups_len); if (res != PPM_SUCCESS) return res; res = __bpf_val_to_ring(data, (unsigned long)data->tmp_scratch, cgroups_len, PT_BYTEBUF, -1, false); if (res != PPM_SUCCESS) return res; bpf_tail_call(data->ctx, &tail_map, PPM_FILLER_proc_startupdate_3); bpf_printk("Can't tail call f_proc_startupdate_3 filler\n"); return PPM_FAILURE_BUG; } FILLER(proc_startupdate_3, true) { struct task_struct *task; struct mm_struct *mm; long retval; int res; retval = bpf_syscall_get_retval(data->ctx); task = (struct task_struct *)bpf_get_current_task(); mm = _READ(task->mm); if (!mm) return PPM_FAILURE_BUG; if (data->state->tail_ctx.evt_type == PPME_SYSCALL_CLONE_20_X || data->state->tail_ctx.evt_type == PPME_SYSCALL_FORK_20_X || data->state->tail_ctx.evt_type == PPME_SYSCALL_VFORK_20_X) { /* * clone-only parameters */ unsigned long flags; struct cred *cred; kuid_t euid; kgid_t egid; pid_t vtid; pid_t vpid; struct pid_namespace *pidns = bpf_task_active_pid_ns(task); int pidns_level = _READ(pidns->level); /* * flags */ if (data->state->tail_ctx.evt_type == PPME_SYSCALL_CLONE_20_X) flags = bpf_syscall_get_argument(data, 0); else flags = 0; flags = clone_flags_to_scap(flags); if(pidns_level != 0) { flags |= PPM_CL_CHILD_IN_PIDNS; } else { struct nsproxy *nsproxy = _READ(task->nsproxy); if(nsproxy) { struct pid_namespace *pid_ns_for_children = _READ(nsproxy->pid_ns_for_children); if(pid_ns_for_children != pidns) { flags |= PPM_CL_CHILD_IN_PIDNS; } } } res = bpf_val_to_ring_type(data, flags, PT_FLAGS32); if (res != PPM_SUCCESS) return res; /* * This logic is wrong and doesn't account for user * namespaces. * Fix this at some point, maybe with a custom BPF * helper. */ cred = (struct cred *)_READ(task->cred); euid = _READ(cred->euid); /* * uid */ res = bpf_val_to_ring_type(data, euid.val, PT_UINT32); if (res != PPM_SUCCESS) return res; egid = _READ(cred->egid); /* * gid */ res = bpf_val_to_ring_type(data, egid.val, PT_UINT32); if (res != PPM_SUCCESS) return res; /* * vtid */ vtid = bpf_task_pid_vnr(task); res = bpf_val_to_ring_type(data, vtid, PT_PID); if (res != PPM_SUCCESS) return res; /* * vpid */ vpid = bpf_task_tgid_vnr(task); res = bpf_val_to_ring_type(data, vpid, PT_PID); } else if (data->state->tail_ctx.evt_type == PPME_SYSCALL_EXECVE_19_X) { /* * execve-only parameters */ long env_len = 0; kuid_t loginuid; int tty; /* * environ */ if (retval >= 0) { /* * Already checked for mm validity */ unsigned long env_end = _READ(mm->env_end); unsigned long env_start = _READ(mm->env_start); env_len = env_end - env_start; if (env_len) { if (env_len > ARGS_ENV_SIZE_MAX) env_len = ARGS_ENV_SIZE_MAX; #ifdef BPF_FORBIDS_ZERO_ACCESS if (bpf_probe_read(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], ((env_len - 1) & SCRATCH_SIZE_HALF) + 1, (void *)env_start)) #else if (bpf_probe_read(&data->buf[data->state->tail_ctx.curoff & SCRATCH_SIZE_HALF], env_len & SCRATCH_SIZE_HALF, (void *)env_start)) #endif env_len = 0; else data->buf[(data->state->tail_ctx.curoff + env_len - 1) & SCRATCH_SIZE_MAX] = 0; } } else { unsigned long val; char **envp; val = bpf_syscall_get_argument(data, 2); envp = (char **)val; res = bpf_accumulate_argv_or_env(data, envp, &env_len); if (res != PPM_SUCCESS) env_len = 0; } data->curarg_already_on_frame = true; res = __bpf_val_to_ring(data, 0, env_len, PT_BYTEBUF, -1, false); if (res != PPM_SUCCESS) return res; /* * tty */ tty = bpf_ppm_get_tty(task); res = bpf_val_to_ring_type(data, tty, PT_INT32); if (res != PPM_SUCCESS) return res; /* * pgid */ res = bpf_val_to_ring_type(data, bpf_task_pgrp_vnr(task), PT_PID); if (res != PPM_SUCCESS) return res; /* * loginuid */ /* TODO: implement user namespace support */ #ifdef COS_73_WORKAROUND { struct audit_task_info* audit = _READ(task->audit); if (audit) { loginuid = _READ(audit->loginuid); } else { loginuid = INVALID_UID; } } #else loginuid = _READ(task->loginuid); #endif res = bpf_val_to_ring_type(data, loginuid.val, PT_INT32); if (res != PPM_SUCCESS) return res; } return res; } FILLER(sys_accept4_e, true) { int res; /* * push the flags into the ring. * XXX we don't support flags yet and so we just return zero */ res = bpf_val_to_ring(data, 0); return res; } FILLER(sys_accept_x, true) { unsigned long max_ack_backlog = 0; unsigned long ack_backlog = 0; unsigned long queuepct = 0; struct socket *sock; long size = 0; int res; int fd; /* * Retrieve the fd and push it to the ring. * Note that, even if we are in the exit callback, the arguments are still * in the stack, and therefore we can consume them. */ fd = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring_type(data, fd, PT_FD); if (res != PPM_SUCCESS) return res; /* * Convert the fd into socket endpoint information */ size = bpf_fd_to_socktuple(data, fd, NULL, 0, false, true, data->tmp_scratch); /* * Copy the endpoint info into the ring */ data->curarg_already_on_frame = true; res = __bpf_val_to_ring(data, 0, size, PT_SOCKTUPLE, -1, false); if (res != PPM_SUCCESS) return res; sock = bpf_sockfd_lookup(data, fd); if (sock) { struct sock *sk = _READ(sock->sk); if (sk) { ack_backlog = _READ(sk->sk_ack_backlog); max_ack_backlog = _READ(sk->sk_max_ack_backlog); if (max_ack_backlog) queuepct = (unsigned long)ack_backlog * 100 / max_ack_backlog; } } /* queuepct */ res = bpf_val_to_ring_type(data, queuepct, PT_UINT8); if (res != PPM_SUCCESS) return res; /* queuelen */ res = bpf_val_to_ring_type(data, ack_backlog, PT_UINT32); if (res != PPM_SUCCESS) return res; /* queuemax */ res = bpf_val_to_ring_type(data, max_ack_backlog, PT_UINT32); return res; } FILLER(sys_setns_e, true) { unsigned long val; u32 flags; int res; val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; val = bpf_syscall_get_argument(data, 1); flags = clone_flags_to_scap(val); res = bpf_val_to_ring(data, flags); return res; } FILLER(sys_unshare_e, true) { unsigned long val; u32 flags; int res; val = bpf_syscall_get_argument(data, 0); flags = clone_flags_to_scap(val); res = bpf_val_to_ring(data, flags); return res; } FILLER(sys_generic, true) { long *sysdig_id; int native_id; int res; native_id = bpf_syscall_get_nr(data->ctx); sysdig_id = bpf_map_lookup_elem(&syscall_code_routing_table, &native_id); if (!sysdig_id) { bpf_printk("no routing for syscall %d\n", native_id); return PPM_FAILURE_BUG; } if (*sysdig_id == PPM_SC_UNKNOWN) bpf_printk("no syscall for id %d\n", native_id); /* * id */ res = bpf_val_to_ring(data, *sysdig_id); if (res != PPM_SUCCESS) return res; if (data->state->tail_ctx.evt_type == PPME_GENERIC_E) { /* * native id */ res = bpf_val_to_ring(data, native_id); } return res; } FILLER(sys_openat_x, true) { unsigned long dev; unsigned long ino; unsigned long flags; unsigned long val; unsigned long mode; long retval; int res; retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * dirfd */ val = bpf_syscall_get_argument(data, 0); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * name */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * Flags * Note that we convert them into the ppm portable representation before pushing them to the ring */ val = bpf_syscall_get_argument(data, 2); flags = open_flags_to_scap(val); res = bpf_val_to_ring(data, flags); if (res != PPM_SUCCESS) return res; /* * mode */ mode = bpf_syscall_get_argument(data, 3); mode = open_modes_to_scap(val, mode); res = bpf_val_to_ring(data, mode); if (res != PPM_SUCCESS) return res; /* * Device */ if (retval < 0 || !bpf_get_fd_dev_ino(retval, &dev, &ino)) dev = 0; res = bpf_val_to_ring(data, dev); return res; } FILLER(sys_sendfile_e, true) { unsigned long val; off_t *offp; off_t off; int res; /* * out_fd */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * in_fd */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * offset */ offp = (off_t *)bpf_syscall_get_argument(data, 2); off = _READ(*offp); res = bpf_val_to_ring(data, off); if (res != PPM_SUCCESS) return res; /* * size */ val = bpf_syscall_get_argument(data, 3); res = bpf_val_to_ring(data, val); return res; } FILLER(sys_sendfile_x, true) { long retval; off_t *offp; off_t off; int res; /* * res */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * offset */ offp = (off_t *)bpf_syscall_get_argument(data, 2); off = _READ(*offp); res = bpf_val_to_ring(data, off); return res; } FILLER(sys_prlimit_e, true) { unsigned long val; u8 ppm_resource; int res; /* * pid */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * resource */ val = bpf_syscall_get_argument(data, 1); ppm_resource = rlimit_resource_to_scap(val); res = bpf_val_to_ring(data, ppm_resource); return res; } FILLER(sys_prlimit_x, true) { unsigned long val; struct rlimit rl; long retval; s64 newcur; s64 newmax; s64 oldcur; s64 oldmax; int res; /* * res */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; /* * Copy the user structure and extract cur and max */ if (retval >= 0) { val = bpf_syscall_get_argument(data, 2); if (bpf_probe_read(&rl, sizeof(rl), (void *)val)) { newcur = -1; newmax = -1; } else { newcur = rl.rlim_cur; newmax = rl.rlim_max; } } else { newcur = -1; newmax = -1; } val = bpf_syscall_get_argument(data, 3); if (bpf_probe_read(&rl, sizeof(rl), (void *)val)) { oldcur = -1; oldmax = -1; } else { oldcur = rl.rlim_cur; oldmax = rl.rlim_max; } /* * newcur */ res = bpf_val_to_ring_type(data, newcur, PT_INT64); if (res != PPM_SUCCESS) return res; /* * newmax */ res = bpf_val_to_ring_type(data, newmax, PT_INT64); if (res != PPM_SUCCESS) return res; /* * oldcur */ res = bpf_val_to_ring_type(data, oldcur, PT_INT64); if (res != PPM_SUCCESS) return res; /* * oldmax */ res = bpf_val_to_ring_type(data, oldmax, PT_INT64); return res; } FILLER(sys_pwritev_e, true) { const struct iovec __user *iov; unsigned long iovcnt; unsigned long val; int res; /* * fd */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; iov = (const struct iovec __user *)bpf_syscall_get_argument(data, 1); iovcnt = bpf_syscall_get_argument(data, 2); res = bpf_parse_readv_writev_bufs(data, iov, iovcnt, 0, PRB_FLAG_PUSH_SIZE | PRB_FLAG_IS_WRITE); if (res != PPM_SUCCESS) return res; val = bpf_syscall_get_argument(data, 3); res = bpf_val_to_ring_type(data, val, PT_UINT64); return res; } FILLER(sys_getresuid_and_gid_x, true) { long retval; u32 *idp; int res; u32 id; /* * return value */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * ruid */ idp = (u32 *)bpf_syscall_get_argument(data, 0); id = _READ(*idp); res = bpf_val_to_ring(data, id); if (res != PPM_SUCCESS) return res; /* * euid */ idp = (u32 *)bpf_syscall_get_argument(data, 1); id = _READ(*idp); res = bpf_val_to_ring(data, id); if (res != PPM_SUCCESS) return res; /* * suid */ idp = (u32 *)bpf_syscall_get_argument(data, 2); id = _READ(*idp); res = bpf_val_to_ring(data, id); return res; } FILLER(sys_socket_bind_x, true) { struct sockaddr *usrsockaddr; unsigned long val; u16 size = 0; int err = 0; long retval; int res; /* * res */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * addr */ usrsockaddr = (struct sockaddr __user *)bpf_syscall_get_argument(data, 1); val = bpf_syscall_get_argument(data, 2); if (usrsockaddr && val != 0) { /* * Copy the address */ err = bpf_addr_to_kernel(usrsockaddr, val, (struct sockaddr *)data->tmp_scratch); if (err >= 0) { /* * Convert the fd into socket endpoint information */ size = bpf_pack_addr(data, (struct sockaddr *)data->tmp_scratch, val); } } /* * Copy the endpoint info into the ring */ data->curarg_already_on_frame = true; res = bpf_val_to_ring_len(data, 0, size); return res; } static __always_inline int f_sys_recv_x_common(struct filler_data *data, long retval) { unsigned long bufsize; unsigned long val; int res; /* * res */ res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; /* * data */ if (retval < 0) { /* * The operation failed, return an empty buffer */ val = 0; bufsize = 0; } else { val = bpf_syscall_get_argument(data, 1); /* * The return value can be lower than the value provided by the user, * and we take that into account. */ bufsize = retval; } data->fd = bpf_syscall_get_argument(data, 0); res = __bpf_val_to_ring(data, val, bufsize, PT_BYTEBUF, -1, true); return res; } FILLER(sys_recv_x, true) { long retval; int res; retval = bpf_syscall_get_retval(data->ctx); res = f_sys_recv_x_common(data, retval); return res; } FILLER(sys_recvfrom_x, true) { struct sockaddr *usrsockaddr; unsigned long val; u16 size = 0; long retval; int addrlen; int err = 0; int res; int fd; /* * Push the common params to the ring */ retval = bpf_syscall_get_retval(data->ctx); res = f_sys_recv_x_common(data, retval); if (res != PPM_SUCCESS) return res; if (retval >= 0) { /* * Get the address */ usrsockaddr = (struct sockaddr *)bpf_syscall_get_argument(data, 4); /* * Get the address len */ val = bpf_syscall_get_argument(data, 5); if (usrsockaddr && val != 0) { if (bpf_probe_read(&addrlen, sizeof(addrlen), (void *)val)) return PPM_FAILURE_INVALID_USER_MEMORY; /* * Copy the address */ err = bpf_addr_to_kernel(usrsockaddr, addrlen, (struct sockaddr *)data->tmp_scratch); if (err >= 0) { fd = bpf_syscall_get_argument(data, 0); /* * Convert the fd into socket endpoint information */ size = bpf_fd_to_socktuple(data, fd, (struct sockaddr *)data->tmp_scratch, addrlen, true, true, data->tmp_scratch + sizeof(struct sockaddr_storage)); } } } /* * Copy the endpoint info into the ring */ data->curarg_already_on_frame = true; res = __bpf_val_to_ring(data, 0, size, PT_SOCKTUPLE, -1, false); return res; } FILLER(sys_shutdown_e, true) { unsigned int flags; unsigned long val; int res; /* * fd */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * how */ val = bpf_syscall_get_argument(data, 1); flags = shutdown_how_to_scap(val); res = bpf_val_to_ring(data, flags); return res; } FILLER(sys_recvmsg_x, true) { const struct iovec *iov; struct user_msghdr mh; unsigned long iovcnt; unsigned long val; long retval; int res; /* * res */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; /* * Retrieve the message header */ val = bpf_syscall_get_argument(data, 1); if (bpf_probe_read(&mh, sizeof(mh), (void *)val)) return PPM_FAILURE_INVALID_USER_MEMORY; /* * data and size */ iov = (const struct iovec *)mh.msg_iov; iovcnt = mh.msg_iovlen; res = bpf_parse_readv_writev_bufs(data, iov, iovcnt, retval, PRB_FLAG_PUSH_ALL); if (res != PPM_SUCCESS) return res; bpf_tail_call(data->ctx, &tail_map, PPM_FILLER_sys_recvmsg_x_2); bpf_printk("Can't tail call f_sys_recvmsg_x_2 filler\n"); return PPM_FAILURE_BUG; } FILLER(sys_recvmsg_x_2, true) { struct sockaddr *usrsockaddr; struct user_msghdr mh; unsigned long val; u16 size = 0; long retval; int addrlen; int res; int fd; retval = bpf_syscall_get_retval(data->ctx); /* * tuple */ if (retval >= 0) { /* * Retrieve the message header */ val = bpf_syscall_get_argument(data, 1); if (bpf_probe_read(&mh, sizeof(mh), (void *)val)) return PPM_FAILURE_INVALID_USER_MEMORY; /* * Get the address */ usrsockaddr = (struct sockaddr *)mh.msg_name; addrlen = mh.msg_namelen; if (usrsockaddr && addrlen != 0) { /* * Copy the address */ res = bpf_addr_to_kernel(usrsockaddr, addrlen, (struct sockaddr *)data->tmp_scratch); if (res >= 0) { fd = bpf_syscall_get_argument(data, 0); /* * Convert the fd into socket endpoint information */ size = bpf_fd_to_socktuple(data, fd, (struct sockaddr *)data->tmp_scratch, addrlen, true, true, data->tmp_scratch + sizeof(struct sockaddr_storage)); } } } data->curarg_already_on_frame = true; res = __bpf_val_to_ring(data, 0, size, PT_SOCKTUPLE, -1, false); return res; } FILLER(sys_sendmsg_e, true) { struct sockaddr *usrsockaddr; const struct iovec *iov; struct user_msghdr mh; unsigned long iovcnt; unsigned long val; u16 size = 0; int addrlen; int err = 0; int res; int fd; /* * fd */ fd = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring_type(data, fd, PT_FD); if (res != PPM_SUCCESS) return res; /* * Retrieve the message header */ val = bpf_syscall_get_argument(data, 1); if (bpf_probe_read(&mh, sizeof(mh), (void *)val)) return PPM_FAILURE_INVALID_USER_MEMORY; /* * size */ iov = (const struct iovec *)mh.msg_iov; iovcnt = mh.msg_iovlen; res = bpf_parse_readv_writev_bufs(data, iov, iovcnt, 0, PRB_FLAG_PUSH_SIZE | PRB_FLAG_IS_WRITE); if (res != PPM_SUCCESS) return res; /* * tuple */ usrsockaddr = (struct sockaddr *)mh.msg_name; addrlen = mh.msg_namelen; if (usrsockaddr && addrlen != 0) { /* * Copy the address */ err = bpf_addr_to_kernel(usrsockaddr, addrlen, (struct sockaddr *)data->tmp_scratch); if (err >= 0) { /* * Convert the fd into socket endpoint information */ size = bpf_fd_to_socktuple(data, fd, (struct sockaddr *)data->tmp_scratch, addrlen, true, false, data->tmp_scratch + sizeof(struct sockaddr_storage)); } } data->curarg_already_on_frame = true; res = __bpf_val_to_ring(data, 0, size, PT_SOCKTUPLE, -1, false); return res; } FILLER(sys_sendmsg_x, true) { const struct iovec *iov; struct user_msghdr mh; unsigned long iovcnt; unsigned long val; long retval; int res; /* * res */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; /* * data */ val = bpf_syscall_get_argument(data, 1); if (bpf_probe_read(&mh, sizeof(mh), (void *)val)) return PPM_FAILURE_INVALID_USER_MEMORY; iov = (const struct iovec *)mh.msg_iov; iovcnt = mh.msg_iovlen; res = bpf_parse_readv_writev_bufs(data, iov, iovcnt, retval, PRB_FLAG_PUSH_DATA | PRB_FLAG_IS_WRITE); return res; } FILLER(sys_creat_x, true) { unsigned long dev; unsigned long ino; unsigned long val; unsigned long mode; long retval; int res; retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * name */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * mode */ mode = bpf_syscall_get_argument(data, 1); mode = open_modes_to_scap(O_CREAT, mode); res = bpf_val_to_ring(data, mode); if (res != PPM_SUCCESS) return res; /* * Device */ if (retval < 0 || !bpf_get_fd_dev_ino(retval, &dev, &ino)) dev = 0; res = bpf_val_to_ring(data, dev); return res; } FILLER(sys_pipe_x, true) { unsigned long ino; unsigned long dev; unsigned long val; long retval; int fds[2]; int res; /* * retval */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * fds */ val = bpf_syscall_get_argument(data, 0); if (bpf_probe_read(fds, sizeof(fds), (void *)val)) return PPM_FAILURE_INVALID_USER_MEMORY; res = bpf_val_to_ring(data, fds[0]); if (res != PPM_SUCCESS) return res; res = bpf_val_to_ring(data, fds[1]); if (res != PPM_SUCCESS) return res; if (!bpf_get_fd_dev_ino(fds[0], &dev, &ino)) ino = 0; res = bpf_val_to_ring(data, ino); return res; } FILLER(sys_lseek_e, true) { unsigned long flags; unsigned long val; int res; /* * fd */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * offset */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * whence */ val = bpf_syscall_get_argument(data, 2); flags = lseek_whence_to_scap(val); res = bpf_val_to_ring(data, flags); return res; } FILLER(sys_llseek_e, true) { unsigned long flags; unsigned long val; unsigned long oh; unsigned long ol; u64 offset; int res; /* * fd */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * offset * We build it by combining the offset_high and offset_low * system call arguments */ oh = bpf_syscall_get_argument(data, 1); ol = bpf_syscall_get_argument(data, 2); offset = (((u64)oh) << 32) + ((u64)ol); res = bpf_val_to_ring(data, offset); if (res != PPM_SUCCESS) return res; /* * whence */ val = bpf_syscall_get_argument(data, 4); flags = lseek_whence_to_scap(val); res = bpf_val_to_ring(data, flags); return res; } FILLER(sys_eventfd_e, true) { unsigned long val; int res; /* * initval */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * flags * XXX not implemented yet */ res = bpf_val_to_ring(data, 0); return res; } FILLER(sys_mount_e, true) { unsigned long val; int res; /* * Fix mount flags in arg 3. * See http://lxr.free-electrons.com/source/fs/namespace.c?v=4.2#L2650 */ val = bpf_syscall_get_argument(data, 3); if ((val & PPM_MS_MGC_MSK) == PPM_MS_MGC_VAL) val &= ~PPM_MS_MGC_MSK; res = bpf_val_to_ring(data, val); return res; } FILLER(sys_ppoll_e, true) { unsigned long val; int res; res = bpf_poll_parse_fds(data, true); if (res != PPM_SUCCESS) return res; /* * timeout */ val = bpf_syscall_get_argument(data, 2); /* NULL timeout specified as 0xFFFFFF.... */ if (val == (unsigned long)NULL) { res = bpf_val_to_ring_type(data, (u64)(-1), PT_RELTIME); if (res != PPM_SUCCESS) return res; } else { res = timespec_parse(data, val); if (res != PPM_SUCCESS) return res; } /* * sigmask */ val = bpf_syscall_get_argument(data, 3); if (val != (unsigned long)NULL) if (bpf_probe_read(&val, sizeof(val), (void *)val)) return PPM_FAILURE_INVALID_USER_MEMORY; res = bpf_val_to_ring_type(data, val, PT_SIGSET); return res; } FILLER(sys_semop_x, true) { unsigned long nsops; struct sembuf *ptr; long retval; int res; /* * return value */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; /* * nsops * actually this could be read in the enter function but * we also need to know the value to access the sembuf structs */ nsops = bpf_syscall_get_argument(data, 2); res = bpf_val_to_ring_type(data, nsops, PT_UINT32); if (res != PPM_SUCCESS) return res; /* * sembuf */ ptr = (struct sembuf *)bpf_syscall_get_argument(data, 1); if (nsops && ptr) { int j; #pragma unroll 2 for (j = 0; j < 2; j++) { struct sembuf sops = {0, 0, 0}; if (nsops--) if (bpf_probe_read(&sops, sizeof(sops), (void *)&ptr[j])) return PPM_FAILURE_INVALID_USER_MEMORY; res = bpf_val_to_ring_type(data, sops.sem_num, PT_UINT16); if (res != PPM_SUCCESS) return res; res = bpf_val_to_ring_type(data, sops.sem_op, PT_INT16); if (res != PPM_SUCCESS) return res; res = bpf_val_to_ring_type(data, semop_flags_to_scap(sops.sem_flg), PT_FLAGS16); if (res != PPM_SUCCESS) return res; } } return res; } FILLER(sys_socket_x, true) { long retval; int res; retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; if (retval > 0 && !data->settings->socket_file_ops) { struct file *file = bpf_fget(retval); if (file) { const struct file_operations *f_op = _READ(file->f_op); data->settings->socket_file_ops = (void *)f_op; } } return res; } FILLER(sys_flock_e, true) { unsigned int flags; unsigned long val; int res; val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; val = bpf_syscall_get_argument(data, 1); flags = flock_flags_to_scap(val); res = bpf_val_to_ring(data, flags); return res; } FILLER(sys_pread64_e, true) { #ifndef _64BIT_ARGS_SINGLE_REGISTER #error Implement this #endif return PPM_FAILURE_BUG; } FILLER(sys_preadv64_e, true) { #ifndef _64BIT_ARGS_SINGLE_REGISTER #error Implement this #endif return PPM_FAILURE_BUG; } FILLER(sys_pwrite64_e, true) { #ifndef _64BIT_ARGS_SINGLE_REGISTER #error Implement this #endif return PPM_FAILURE_BUG; } FILLER(sys_renameat_x, true) { unsigned long val; long retval; int res; retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * olddirfd */ val = bpf_syscall_get_argument(data, 0); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * oldpath */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * newdirfd */ val = bpf_syscall_get_argument(data, 2); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * newpath */ val = bpf_syscall_get_argument(data, 3); res = bpf_val_to_ring(data, val); return res; } FILLER(sys_symlinkat_x, true) { unsigned long val; long retval; int res; retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; /* * oldpath */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring_type(data, val, PT_CHARBUF); if (res != PPM_SUCCESS) return res; /* * newdirfd */ val = bpf_syscall_get_argument(data, 1); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = bpf_val_to_ring_type(data, val, PT_FD); if (res != PPM_SUCCESS) return res; /* * newpath */ val = bpf_syscall_get_argument(data, 2); res = bpf_val_to_ring_type(data, val, PT_CHARBUF); return res; } FILLER(sys_sysdigevent_e, false) { bpf_printk("f_sys_sysdigevent_e should never be called\n"); return PPM_FAILURE_BUG; } FILLER(cpu_hotplug_e, false) { int res; res = bpf_val_to_ring(data, data->state->hotplug_cpu); if (res != PPM_SUCCESS) return res; res = bpf_val_to_ring(data, 0); if (res != PPM_SUCCESS) return res; data->state->hotplug_cpu = 0; return res; } FILLER(sched_drop, false) { int res; /* * ratio */ res = bpf_val_to_ring(data, data->settings->sampling_ratio); return res; } FILLER(sys_procexit_e, false) { struct task_struct *task; unsigned int flags; int exit_code; int res; task = (struct task_struct *)bpf_get_current_task(); exit_code = _READ(task->exit_code); res = bpf_val_to_ring(data, exit_code); if (res != PPM_SUCCESS) return res; #ifndef BPF_SUPPORTS_RAW_TRACEPOINTS delete_args(); #endif return res; } FILLER(sched_switch_e, false) { struct sched_switch_args *ctx; struct task_struct *task; unsigned long total_vm; unsigned long maj_flt; unsigned long min_flt; struct mm_struct *mm; pid_t next_pid; long total_rss; long swap; int res; ctx = (struct sched_switch_args *)data->ctx; #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS struct task_struct *next_task = (struct task_struct *)ctx->next; next_pid = _READ(next_task->pid); #else next_pid = ctx->next_pid; #endif /* * next */ res = bpf_val_to_ring_type(data, next_pid, PT_PID); if (res != PPM_SUCCESS) return res; task = (struct task_struct *)bpf_get_current_task(); /* * pgft_maj */ maj_flt = _READ(task->maj_flt); res = bpf_val_to_ring_type(data, maj_flt, PT_UINT64); if (res != PPM_SUCCESS) return res; /* * pgft_min */ min_flt = _READ(task->min_flt); res = bpf_val_to_ring_type(data, min_flt, PT_UINT64); if (res != PPM_SUCCESS) return res; total_vm = 0; total_rss = 0; swap = 0; mm = _READ(task->mm); if (mm) { total_vm = _READ(mm->total_vm); total_vm <<= (PAGE_SHIFT - 10); total_rss = bpf_get_mm_rss(mm) << (PAGE_SHIFT - 10); swap = bpf_get_mm_swap(mm) << (PAGE_SHIFT - 10); } /* * vm_size */ res = bpf_val_to_ring_type(data, total_vm, PT_UINT32); if (res != PPM_SUCCESS) return res; /* * vm_rss */ res = bpf_val_to_ring_type(data, total_rss, PT_UINT32); if (res != PPM_SUCCESS) return res; /* * vm_swap */ res = bpf_val_to_ring_type(data, swap, PT_UINT32); return res; } FILLER(sys_pagefault_e, false) { struct page_fault_args *ctx; unsigned long error_code; unsigned long address; unsigned long ip; u32 flags; int res; ctx = (struct page_fault_args *)data->ctx; #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS struct pt_regs *regs = (struct pt_regs *)ctx->regs; address = ctx->address; ip = _READ(regs->ip); error_code = ctx->error_code; #else address = ctx->address; ip = ctx->ip; error_code = ctx->error_code; #endif res = bpf_val_to_ring(data, address); if (res != PPM_SUCCESS) return res; res = bpf_val_to_ring(data, ip); if (res != PPM_SUCCESS) return res; flags = pf_flags_to_scap(error_code); res = bpf_val_to_ring(data, flags); return res; } FILLER(sys_signaldeliver_e, false) { struct signal_deliver_args *ctx; pid_t spid = 0; int sig; int res; ctx = (struct signal_deliver_args *)data->ctx; #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS struct siginfo *info = (struct siginfo *)ctx->info; sig = ctx->sig; if (sig == SIGKILL) { spid = _READ(info->_sifields._kill._pid); } else if (sig == SIGTERM || sig == SIGHUP || sig == SIGINT || sig == SIGTSTP || sig == SIGQUIT) { int si_code = _READ(info->si_code); if (si_code == SI_USER || si_code == SI_QUEUE || si_code <= 0) { spid = _READ(info->si_pid); } } else if (sig == SIGCHLD) { spid = _READ(info->_sifields._sigchld._pid); } else if (sig >= SIGRTMIN && sig <= SIGRTMAX) { spid = _READ(info->_sifields._rt._pid); } #else sig = ctx->sig; #endif /* * source pid */ res = bpf_val_to_ring(data, spid); if (res != PPM_SUCCESS) return res; /* * destination pid */ res = bpf_val_to_ring(data, bpf_get_current_pid_tgid() & 0xffffffff); if (res != PPM_SUCCESS) return res; /* * signal number */ res = bpf_val_to_ring(data, sig); return res; } FILLER(sys_quotactl_e, true) { unsigned long val; int res; u32 id; u8 quota_fmt; u16 cmd; /* * extract cmd */ val = bpf_syscall_get_argument(data, 0); cmd = quotactl_cmd_to_scap(val); res = bpf_val_to_ring_type(data, cmd, PT_FLAGS16); if (res != PPM_SUCCESS) return res; /* * extract type */ res = bpf_val_to_ring_type(data, quotactl_type_to_scap(val), PT_FLAGS8); if (res != PPM_SUCCESS) return res; /* * extract id */ id = 0; val = bpf_syscall_get_argument(data, 2); if (cmd == PPM_Q_GETQUOTA || cmd == PPM_Q_SETQUOTA || cmd == PPM_Q_XGETQUOTA || cmd == PPM_Q_XSETQLIM) { /* * in this case id represent an userid or groupid so add it */ id = val; } res = bpf_val_to_ring_type(data, id, PT_UINT32); if (res != PPM_SUCCESS) return res; /* * extract quota_fmt from id */ quota_fmt = PPM_QFMT_NOT_USED; if (cmd == PPM_Q_QUOTAON) quota_fmt = quotactl_fmt_to_scap(val); res = bpf_val_to_ring_type(data, quota_fmt, PT_FLAGS8); return res; } FILLER(sys_quotactl_x, true) { struct if_dqinfo dqinfo = {0}; struct if_dqblk dqblk = {0}; const char empty[] = ""; u32 quota_fmt_out; unsigned long val; long retval; int res; u16 cmd; /* * extract cmd */ val = bpf_syscall_get_argument(data, 0); cmd = quotactl_cmd_to_scap(val); /* * return value */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; /* * Add special */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring_type(data, val, PT_CHARBUF); if (res != PPM_SUCCESS) return res; /* * get addr */ val = bpf_syscall_get_argument(data, 3); /* * get quotafilepath only for QUOTAON */ if (cmd == PPM_Q_QUOTAON) { res = bpf_val_to_ring_type(data, val, PT_CHARBUF); if (res != PPM_SUCCESS) return res; } else { res = bpf_val_to_ring_type(data, (unsigned long)empty, PT_CHARBUF); if (res != PPM_SUCCESS) return res; } /* * dqblk fields if present */ if (cmd == PPM_Q_GETQUOTA || cmd == PPM_Q_SETQUOTA) { if (bpf_probe_read(&dqblk, sizeof(dqblk), (void *)val)) return PPM_FAILURE_INVALID_USER_MEMORY; } if (dqblk.dqb_valid & QIF_BLIMITS) { res = bpf_val_to_ring_type(data, dqblk.dqb_bhardlimit, PT_UINT64); if (res != PPM_SUCCESS) return res; res = bpf_val_to_ring_type(data, dqblk.dqb_bsoftlimit, PT_UINT64); if (res != PPM_SUCCESS) return res; } else { res = bpf_val_to_ring_type(data, 0, PT_UINT64); if (res != PPM_SUCCESS) return res; res = bpf_val_to_ring_type(data, 0, PT_UINT64); if (res != PPM_SUCCESS) return res; } if (dqblk.dqb_valid & QIF_SPACE) { res = bpf_val_to_ring_type(data, dqblk.dqb_curspace, PT_UINT64); if (res != PPM_SUCCESS) return res; } else { res = bpf_val_to_ring_type(data, 0, PT_UINT64); if (res != PPM_SUCCESS) return res; } if (dqblk.dqb_valid & QIF_ILIMITS) { res = bpf_val_to_ring_type(data, dqblk.dqb_ihardlimit, PT_UINT64); if (res != PPM_SUCCESS) return res; res = bpf_val_to_ring_type(data, dqblk.dqb_isoftlimit, PT_UINT64); if (res != PPM_SUCCESS) return res; } else { res = bpf_val_to_ring_type(data, 0, PT_UINT64); if (res != PPM_SUCCESS) return res; res = bpf_val_to_ring_type(data, 0, PT_UINT64); if (res != PPM_SUCCESS) return res; } if (dqblk.dqb_valid & QIF_BTIME) { res = bpf_val_to_ring_type(data, dqblk.dqb_btime, PT_RELTIME); if (res != PPM_SUCCESS) return res; } else { res = bpf_val_to_ring_type(data, 0, PT_RELTIME); if (res != PPM_SUCCESS) return res; } if (dqblk.dqb_valid & QIF_ITIME) { res = bpf_val_to_ring_type(data, dqblk.dqb_itime, PT_RELTIME); if (res != PPM_SUCCESS) return res; } else { res = bpf_val_to_ring_type(data, 0, PT_RELTIME); if (res != PPM_SUCCESS) return res; } /* * dqinfo fields if present */ if (cmd == PPM_Q_GETINFO || cmd == PPM_Q_SETINFO) { if (bpf_probe_read(&dqinfo, sizeof(dqinfo), (void *)val)) return PPM_FAILURE_INVALID_USER_MEMORY; } if (dqinfo.dqi_valid & IIF_BGRACE) { res = bpf_val_to_ring_type(data, dqinfo.dqi_bgrace, PT_RELTIME); if (res != PPM_SUCCESS) return res; } else { res = bpf_val_to_ring_type(data, 0, PT_RELTIME); if (res != PPM_SUCCESS) return res; } if (dqinfo.dqi_valid & IIF_IGRACE) { res = bpf_val_to_ring_type(data, dqinfo.dqi_igrace, PT_RELTIME); if (res != PPM_SUCCESS) return res; } else { res = bpf_val_to_ring_type(data, 0, PT_RELTIME); if (res != PPM_SUCCESS) return res; } if (dqinfo.dqi_valid & IIF_FLAGS) { res = bpf_val_to_ring_type(data, dqinfo.dqi_flags, PT_FLAGS8); if (res != PPM_SUCCESS) return res; } else { res = bpf_val_to_ring_type(data, 0, PT_FLAGS8); if (res != PPM_SUCCESS) return res; } quota_fmt_out = PPM_QFMT_NOT_USED; if (cmd == PPM_Q_GETFMT) { u32 tmp; if (bpf_probe_read(&tmp, sizeof(tmp), (void *)val)) return PPM_FAILURE_INVALID_USER_MEMORY; quota_fmt_out = quotactl_fmt_to_scap(tmp); } res = bpf_val_to_ring_type(data, quota_fmt_out, PT_FLAGS8); return res; } FILLER(sys_semget_e, true) { unsigned long val; int res; /* * key */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * nsems */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * semflg */ val = bpf_syscall_get_argument(data, 2); res = bpf_val_to_ring(data, semget_flags_to_scap(val)); return res; } FILLER(sys_semctl_e, true) { unsigned long val; int res; /* * semid */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * semnum */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * cmd */ val = bpf_syscall_get_argument(data, 2); res = bpf_val_to_ring(data, semctl_cmd_to_scap(val)); if (res != PPM_SUCCESS) return res; /* * optional argument semun/val */ if (val == SETVAL) val = bpf_syscall_get_argument(data, 3); else val = 0; res = bpf_val_to_ring(data, val); return res; } FILLER(sys_ptrace_e, true) { unsigned long val; int res; /* * request */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, ptrace_requests_to_scap(val)); if (res != PPM_SUCCESS) return res; /* * pid */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, val); return res; } static __always_inline int bpf_parse_ptrace_addr(struct filler_data *data, u16 request) { enum ppm_param_type type; unsigned long val; u8 idx; val = bpf_syscall_get_argument(data, 2); switch (request) { default: idx = PPM_PTRACE_IDX_UINT64; type = PT_UINT64; } return bpf_val_to_ring_dyn(data, val, type, idx); } static __always_inline int bpf_parse_ptrace_data(struct filler_data *data, u16 request) { enum ppm_param_type type; unsigned long val; u64 dst; u8 idx; val = bpf_syscall_get_argument(data, 3); switch (request) { case PPM_PTRACE_PEEKTEXT: case PPM_PTRACE_PEEKDATA: case PPM_PTRACE_PEEKUSR: idx = PPM_PTRACE_IDX_UINT64; type = PT_UINT64; if (bpf_probe_read(&dst, sizeof(long), (void *)val)) return PPM_FAILURE_INVALID_USER_MEMORY; break; case PPM_PTRACE_CONT: case PPM_PTRACE_SINGLESTEP: case PPM_PTRACE_DETACH: case PPM_PTRACE_SYSCALL: idx = PPM_PTRACE_IDX_SIGTYPE; type = PT_SIGTYPE; dst = (u64)val; break; case PPM_PTRACE_ATTACH: case PPM_PTRACE_TRACEME: case PPM_PTRACE_POKETEXT: case PPM_PTRACE_POKEDATA: case PPM_PTRACE_POKEUSR: default: idx = PPM_PTRACE_IDX_UINT64; type = PT_UINT64; dst = (u64)val; break; } return bpf_val_to_ring_dyn(data, dst, type, idx); } FILLER(sys_ptrace_x, true) { unsigned long val; u16 request; long retval; int res; /* * res */ retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring_type(data, retval, PT_ERRNO); if (res != PPM_SUCCESS) return res; if (retval < 0) { res = bpf_val_to_ring_dyn(data, 0, PT_UINT64, 0); if (res != PPM_SUCCESS) return res; res = bpf_val_to_ring_dyn(data, 0, PT_UINT64, 0); return res; } val = bpf_syscall_get_argument(data, 0); request = ptrace_requests_to_scap(val); res = bpf_parse_ptrace_addr(data, request); if (res != PPM_SUCCESS) return res; res = bpf_parse_ptrace_data(data, request); return res; } FILLER(sys_bpf_x, true) { unsigned long cmd; s64 retval; int res; retval = bpf_syscall_get_retval(data->ctx); cmd = bpf_syscall_get_argument(data, 0); /* * fd, depending on cmd */ if (retval >= 0 && (cmd == BPF_MAP_CREATE || cmd == BPF_PROG_LOAD)) res = bpf_val_to_ring_dyn(data, retval, PT_FD, PPM_BPF_IDX_FD); else res = bpf_val_to_ring_dyn(data, retval, PT_ERRNO, PPM_BPF_IDX_RES); return res; } FILLER(sys_unlinkat_x, true) { unsigned long val; long retval; int res; retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * dirfd */ val = bpf_syscall_get_argument(data, 0); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * name */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * flags */ val = bpf_syscall_get_argument(data, 2); res = bpf_val_to_ring(data, unlinkat_flags_to_scap(val)); return res; } FILLER(sys_mkdirat_x, true) { unsigned long val; long retval; int res; retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * dirfd */ val = bpf_syscall_get_argument(data, 0); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * path */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * mode */ val = bpf_syscall_get_argument(data, 2); res = bpf_val_to_ring(data, val); return res; } FILLER(sys_linkat_x, true) { unsigned long val; long retval; int res; retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * olddir */ val = bpf_syscall_get_argument(data, 0); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * oldpath */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * newdir */ val = bpf_syscall_get_argument(data, 2); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * newpath */ val = bpf_syscall_get_argument(data, 3); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * flags */ val = bpf_syscall_get_argument(data, 4); res = bpf_val_to_ring(data, linkat_flags_to_scap(val)); return res; } FILLER(sys_autofill, true) { const struct ppm_event_entry *evinfo; int res; int j; evinfo = data->filler_info; #pragma unroll for (j = 0; j < PPM_MAX_AUTOFILL_ARGS; j++) { struct ppm_autofill_arg arg = evinfo->autofill_args[j]; unsigned long val; if (j == evinfo->n_autofill_args) break; if (arg.id >= 0) val = bpf_syscall_get_argument(data, arg.id); else if (arg.id == AF_ID_RETVAL) val = bpf_syscall_get_retval(data->ctx); else if (arg.id == AF_ID_USEDEFAULT) val = arg.default_val; res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; } return res; } FILLER(sys_fchmodat_x, true) { unsigned long val; int res; long retval; unsigned int mode; retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * dirfd */ val = bpf_syscall_get_argument(data, 0); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * filename */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * mode */ mode = bpf_syscall_get_argument(data, 2); mode = chmod_mode_to_scap(mode); res = bpf_val_to_ring(data, mode); return res; } FILLER(sys_chmod_x, true) { unsigned long val; int res; long retval; retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * filename */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * mode */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, val); return res; } FILLER(sys_fchmod_x, true) { unsigned long val; int res; long retval; retval = bpf_syscall_get_retval(data->ctx); res = bpf_val_to_ring(data, retval); if (res != PPM_SUCCESS) return res; /* * fd */ val = bpf_syscall_get_argument(data, 0); res = bpf_val_to_ring(data, val); if (res != PPM_SUCCESS) return res; /* * mode */ val = bpf_syscall_get_argument(data, 1); res = bpf_val_to_ring(data, val); return res; } #endif sysdig-0.26.4/driver/bpf/maps.h000066400000000000000000000044171352731327100162720ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifndef __MAPS_H #define __MAPS_H #include "types.h" struct bpf_map_def __bpf_section("maps") perf_map = { .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(u32), .max_entries = 0, }; struct bpf_map_def __bpf_section("maps") tail_map = { .type = BPF_MAP_TYPE_PROG_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(u32), .max_entries = PPM_FILLER_MAX, }; struct bpf_map_def __bpf_section("maps") syscall_code_routing_table = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(u64), .max_entries = SYSCALL_TABLE_SIZE, }; struct bpf_map_def __bpf_section("maps") syscall_table = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(struct syscall_evt_pair), .max_entries = SYSCALL_TABLE_SIZE, }; struct bpf_map_def __bpf_section("maps") event_info_table = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(struct ppm_event_info), .max_entries = PPM_EVENT_MAX, }; struct bpf_map_def __bpf_section("maps") fillers_table = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(struct ppm_event_entry), .max_entries = PPM_EVENT_MAX, }; struct bpf_map_def __bpf_section("maps") frame_scratch_map = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = SCRATCH_SIZE, .max_entries = 0, }; struct bpf_map_def __bpf_section("maps") tmp_scratch_map = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = SCRATCH_SIZE, .max_entries = 0, }; struct bpf_map_def __bpf_section("maps") settings_map = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(struct sysdig_bpf_settings), .max_entries = 1, }; struct bpf_map_def __bpf_section("maps") local_state_map = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(struct sysdig_bpf_per_cpu_state), .max_entries = 0, }; #ifndef BPF_SUPPORTS_RAW_TRACEPOINTS struct bpf_map_def __bpf_section("maps") stash_map = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(u64), .value_size = sizeof(struct sys_stash_args), .max_entries = 65535, }; #endif #endif sysdig-0.26.4/driver/bpf/plumbing_helpers.h000066400000000000000000000237311352731327100206710ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifndef __PLUMBING_HELPERS_H #define __PLUMBING_HELPERS_H #include #include #include #include "types.h" #define _READ(P) ({ typeof(P) _val; \ memset(&_val, 0, sizeof(_val)); \ bpf_probe_read(&_val, sizeof(_val), &P); \ _val; \ }) #ifdef BPF_DEBUG #define bpf_printk(fmt, ...) \ do { \ char s[] = fmt; \ bpf_trace_printk(s, sizeof(s), ##__VA_ARGS__); \ } while (0) #else #define bpf_printk(fmt, ...) #endif #ifndef BPF_SUPPORTS_RAW_TRACEPOINTS static __always_inline int __stash_args(unsigned long long id, unsigned long *args) { int ret = bpf_map_update_elem(&stash_map, &id, args, BPF_ANY); if (ret) bpf_printk("error stashing arguments for %d:%d\n", id, ret); return ret; } static __always_inline int stash_args(unsigned long *args) { unsigned long long id = bpf_get_current_pid_tgid() & 0xffffffff; return __stash_args(id, args); } static __always_inline unsigned long *__unstash_args(unsigned long long id) { struct sys_stash_args *args; args = bpf_map_lookup_elem(&stash_map, &id); if (!args) return NULL; return args->args; } static __always_inline unsigned long *unstash_args(void) { unsigned long long id = bpf_get_current_pid_tgid() & 0xffffffff; return __unstash_args(id); } static __always_inline void delete_args(void) { unsigned long long id = bpf_get_current_pid_tgid() & 0xffffffff; bpf_map_delete_elem(&stash_map, &id); } #endif /* Can be called just from an exit event */ static __always_inline long bpf_syscall_get_retval(void *ctx) { struct sys_exit_args *args = (struct sys_exit_args *)ctx; return args->ret; } /* Can be called from both enter and exit event, id is at the same * offset in both struct sys_enter_args and struct sys_exit_args */ static __always_inline long bpf_syscall_get_nr(void *ctx) { struct sys_enter_args *args = (struct sys_enter_args *)ctx; long id; #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS struct pt_regs *regs = (struct pt_regs *)args->regs; id = _READ(regs->orig_ax); #else id = args->id; #endif return id; } #ifndef BPF_SUPPORTS_RAW_TRACEPOINTS static __always_inline unsigned long bpf_syscall_get_argument_from_args(unsigned long *args, int idx) { unsigned long arg; if (idx <= 5) arg = args[idx]; else arg = 0; return arg; } #endif static __always_inline unsigned long bpf_syscall_get_argument_from_ctx(void *ctx, int idx) { unsigned long arg; #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS struct sys_enter_args *args = (struct sys_enter_args *)ctx; struct pt_regs *regs = (struct pt_regs *)args->regs; switch (idx) { case 0: arg = _READ(regs->di); break; case 1: arg = _READ(regs->si); break; case 2: arg = _READ(regs->dx); break; case 3: arg = _READ(regs->r10); break; case 4: arg = _READ(regs->r8); break; case 5: arg = _READ(regs->r9); break; default: arg = 0; } #else unsigned long *args = unstash_args(); if (args) arg = bpf_syscall_get_argument_from_args(args, idx); else arg = 0; #endif return arg; } static __always_inline unsigned long bpf_syscall_get_argument(struct filler_data *data, int idx) { #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS return bpf_syscall_get_argument_from_ctx(data->ctx, idx); #else return bpf_syscall_get_argument_from_args(data->args, idx); #endif } static __always_inline char *get_frame_scratch_area(unsigned int cpu) { char *scratchp; scratchp = bpf_map_lookup_elem(&frame_scratch_map, &cpu); if (!scratchp) bpf_printk("frame scratch NULL\n"); return scratchp; } static __always_inline char *get_tmp_scratch_area(unsigned int cpu) { char *scratchp; scratchp = bpf_map_lookup_elem(&tmp_scratch_map, &cpu); if (!scratchp) bpf_printk("tmp scratch NULL\n"); return scratchp; } static __always_inline const struct syscall_evt_pair *get_syscall_info(int id) { const struct syscall_evt_pair *p = bpf_map_lookup_elem(&syscall_table, &id); if (!p) bpf_printk("no syscall_info for %d\n", id); return p; } static __always_inline const struct ppm_event_info *get_event_info(enum ppm_event_type event_type) { const struct ppm_event_info *e = bpf_map_lookup_elem(&event_info_table, &event_type); if (!e) bpf_printk("no event info for %d\n", event_type); return e; } static __always_inline const struct ppm_event_entry *get_event_filler_info(enum ppm_event_type event_type) { const struct ppm_event_entry *e; e = bpf_map_lookup_elem(&fillers_table, &event_type); if (!e) bpf_printk("no filler info for %d\n", event_type); return e; } static __always_inline struct sysdig_bpf_settings *get_bpf_settings(void) { struct sysdig_bpf_settings *settings; int id = 0; settings = bpf_map_lookup_elem(&settings_map, &id); if (!settings) bpf_printk("settings NULL\n"); return settings; } static __always_inline struct sysdig_bpf_per_cpu_state *get_local_state(unsigned int cpu) { struct sysdig_bpf_per_cpu_state *state; state = bpf_map_lookup_elem(&local_state_map, &cpu); if (!state) bpf_printk("state NULL\n"); return state; } static __always_inline bool acquire_local_state(struct sysdig_bpf_per_cpu_state *state) { if (state->in_use) { bpf_printk("acquire_local_state: already in use\n"); return false; } state->in_use = true; return true; } static __always_inline bool release_local_state(struct sysdig_bpf_per_cpu_state *state) { if (!state->in_use) { bpf_printk("release_local_state: already not in use\n"); return false; } state->in_use = false; return true; } static __always_inline int init_filler_data(void *ctx, struct filler_data *data, bool is_syscall) { unsigned int cpu; data->ctx = ctx; data->settings = get_bpf_settings(); if (!data->settings) return PPM_FAILURE_BUG; cpu = bpf_get_smp_processor_id(); data->buf = get_frame_scratch_area(cpu); if (!data->buf) return PPM_FAILURE_BUG; data->state = get_local_state(cpu); if (!data->state) return PPM_FAILURE_BUG; data->tmp_scratch = get_tmp_scratch_area(cpu); if (!data->tmp_scratch) return PPM_FAILURE_BUG; data->evt = get_event_info(data->state->tail_ctx.evt_type); if (!data->evt) return PPM_FAILURE_BUG; data->filler_info = get_event_filler_info(data->state->tail_ctx.evt_type); if (!data->filler_info) return PPM_FAILURE_BUG; #ifndef BPF_SUPPORTS_RAW_TRACEPOINTS if (is_syscall) { data->args = unstash_args(); if (!data->args) return PPM_SKIP_EVENT; } #endif data->curarg_already_on_frame = false; data->fd = -1; return PPM_SUCCESS; } static __always_inline int bpf_test_bit(int nr, unsigned long *addr) { return 1UL & (_READ(addr[BIT_WORD(nr)]) >> (nr & (BITS_PER_LONG - 1))); } static __always_inline bool drop_event(void *ctx, struct sysdig_bpf_per_cpu_state *state, enum ppm_event_type evt_type, struct sysdig_bpf_settings *settings, enum syscall_flags drop_flags) { if (!settings->dropping_mode) return false; switch (evt_type) { case PPME_SYSCALL_CLOSE_X: case PPME_SOCKET_BIND_X: { long ret = bpf_syscall_get_retval(ctx); if (ret < 0) return true; break; } case PPME_SYSCALL_CLOSE_E: { struct sys_enter_args *args; struct files_struct *files; struct task_struct *task; unsigned long *open_fds; struct fdtable *fdt; int close_fd; int max_fds; close_fd = bpf_syscall_get_argument_from_ctx(ctx, 0); if (close_fd < 0) return true; task = (struct task_struct *)bpf_get_current_task(); if (!task) break; files = _READ(task->files); if (!files) break; fdt = _READ(files->fdt); if (!fdt) break; max_fds = _READ(fdt->max_fds); if (close_fd >= max_fds) return true; open_fds = _READ(fdt->open_fds); if (!open_fds) break; if (!bpf_test_bit(close_fd, open_fds)) return true; break; } case PPME_SYSCALL_FCNTL_E: case PPME_SYSCALL_FCNTL_X: { long cmd = bpf_syscall_get_argument_from_ctx(ctx, 1); if (cmd != F_DUPFD && cmd != F_DUPFD_CLOEXEC) return true; break; } default: break; } if (drop_flags & UF_NEVER_DROP) return false; if (drop_flags & UF_ALWAYS_DROP) return true; if (state->tail_ctx.ts % 1000000000 >= 1000000000 / settings->sampling_ratio) { if (!settings->is_dropping) { settings->is_dropping = true; state->tail_ctx.evt_type = PPME_DROP_E; return false; } return true; } if (settings->is_dropping) { settings->is_dropping = false; state->tail_ctx.evt_type = PPME_DROP_X; return false; } return false; } static __always_inline void reset_tail_ctx(struct sysdig_bpf_per_cpu_state *state, enum ppm_event_type evt_type, unsigned long long ts) { state->tail_ctx.evt_type = evt_type; state->tail_ctx.ts = ts; state->tail_ctx.curarg = 0; state->tail_ctx.curoff = 0; state->tail_ctx.len = 0; state->tail_ctx.prev_res = 0; } static __always_inline void call_filler(void *ctx, void *stack_ctx, enum ppm_event_type evt_type, struct sysdig_bpf_settings *settings, enum syscall_flags drop_flags) { const struct ppm_event_entry *filler_info; struct sysdig_bpf_per_cpu_state *state; unsigned long long pid; unsigned long long ts; unsigned int cpu; cpu = bpf_get_smp_processor_id(); state = get_local_state(cpu); if (!state) return; if (!acquire_local_state(state)) return; if (cpu == 0 && state->hotplug_cpu != 0) { evt_type = PPME_CPU_HOTPLUG_E; drop_flags = UF_NEVER_DROP; } ts = settings->boot_time + bpf_ktime_get_ns(); reset_tail_ctx(state, evt_type, ts); /* drop_event can change state->tail_ctx.evt_type */ if (drop_event(stack_ctx, state, evt_type, settings, drop_flags)) goto cleanup; ++state->n_evts; filler_info = get_event_filler_info(state->tail_ctx.evt_type); if (!filler_info) goto cleanup; bpf_tail_call(ctx, &tail_map, filler_info->filler_id); bpf_printk("Can't tail call filler evt=%d, filler=%d\n", state->tail_ctx.evt_type, filler_info->filler_id); cleanup: release_local_state(state); } #endif sysdig-0.26.4/driver/bpf/probe.c000066400000000000000000000116271352731327100164350ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #include "quirks.h" #include #include #include #include "../driver_config.h" #include "../ppm_events_public.h" #include "bpf_helpers.h" #include "types.h" #include "maps.h" #include "plumbing_helpers.h" #include "ring_helpers.h" #include "filler_helpers.h" #include "fillers.h" #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS #define BPF_PROBE(prefix, event, type) \ __bpf_section(TP_NAME #event) \ int bpf_##event(struct type *ctx) #else #define BPF_PROBE(prefix, event, type) \ __bpf_section(TP_NAME prefix #event) \ int bpf_##event(struct type *ctx) #endif BPF_PROBE("raw_syscalls/", sys_enter, sys_enter_args) { const struct syscall_evt_pair *sc_evt; struct sysdig_bpf_settings *settings; enum ppm_event_type evt_type; int drop_flags; long id; if (bpf_in_ia32_syscall()) return 0; id = bpf_syscall_get_nr(ctx); if (id < 0 || id >= SYSCALL_TABLE_SIZE) return 0; settings = get_bpf_settings(); if (!settings) return 0; if (!settings->capture_enabled) return 0; sc_evt = get_syscall_info(id); if (!sc_evt) return 0; if (sc_evt->flags & UF_USED) { evt_type = sc_evt->enter_event_type; drop_flags = sc_evt->flags; } else { evt_type = PPME_GENERIC_E; drop_flags = UF_ALWAYS_DROP; } #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS call_filler(ctx, ctx, evt_type, settings, drop_flags); #else /* Duplicated here to avoid verifier madness */ struct sys_enter_args stack_ctx; memcpy(stack_ctx.args, ctx->args, sizeof(ctx->args)); if (stash_args(stack_ctx.args)) return 0; call_filler(ctx, &stack_ctx, evt_type, settings, drop_flags); #endif return 0; } BPF_PROBE("raw_syscalls/", sys_exit, sys_exit_args) { const struct syscall_evt_pair *sc_evt; struct sysdig_bpf_settings *settings; enum ppm_event_type evt_type; int drop_flags; long id; if (bpf_in_ia32_syscall()) return 0; id = bpf_syscall_get_nr(ctx); if (id < 0 || id >= SYSCALL_TABLE_SIZE) return 0; settings = get_bpf_settings(); if (!settings) return 0; if (!settings->capture_enabled) return 0; sc_evt = get_syscall_info(id); if (!sc_evt) return 0; if (sc_evt->flags & UF_USED) { evt_type = sc_evt->exit_event_type; drop_flags = sc_evt->flags; } else { evt_type = PPME_GENERIC_X; drop_flags = UF_ALWAYS_DROP; } call_filler(ctx, ctx, evt_type, settings, drop_flags); return 0; } BPF_PROBE("sched/", sched_process_exit, sched_process_exit_args) { struct sysdig_bpf_settings *settings; enum ppm_event_type evt_type; struct task_struct *task; unsigned int flags; task = (struct task_struct *)bpf_get_current_task(); flags = _READ(task->flags); if (flags & PF_KTHREAD) return 0; settings = get_bpf_settings(); if (!settings) return 0; if (!settings->capture_enabled) return 0; evt_type = PPME_PROCEXIT_1_E; call_filler(ctx, ctx, evt_type, settings, UF_NEVER_DROP); return 0; } BPF_PROBE("sched/", sched_switch, sched_switch_args) { struct sysdig_bpf_settings *settings; enum ppm_event_type evt_type; settings = get_bpf_settings(); if (!settings) return 0; if (!settings->capture_enabled) return 0; evt_type = PPME_SCHEDSWITCH_6_E; call_filler(ctx, ctx, evt_type, settings, 0); return 0; } static __always_inline int bpf_page_fault(struct page_fault_args *ctx) { struct sysdig_bpf_settings *settings; enum ppm_event_type evt_type; settings = get_bpf_settings(); if (!settings) return 0; if (!settings->page_faults) return 0; if (!settings->capture_enabled) return 0; evt_type = PPME_PAGE_FAULT_E; call_filler(ctx, ctx, evt_type, settings, UF_ALWAYS_DROP); return 0; } BPF_PROBE("exceptions/", page_fault_user, page_fault_args) { return bpf_page_fault(ctx); } BPF_PROBE("exceptions/", page_fault_kernel, page_fault_args) { return bpf_page_fault(ctx); } BPF_PROBE("signal/", signal_deliver, signal_deliver_args) { struct sysdig_bpf_settings *settings; enum ppm_event_type evt_type; settings = get_bpf_settings(); if (!settings) return 0; if (!settings->capture_enabled) return 0; evt_type = PPME_SIGNALDELIVER_E; call_filler(ctx, ctx, evt_type, settings, UF_ALWAYS_DROP); return 0; } #ifndef BPF_SUPPORTS_RAW_TRACEPOINTS __bpf_section(TP_NAME "sched/sched_process_fork") int bpf_sched_process_fork(struct sched_process_fork_args *ctx) { struct sysdig_bpf_settings *settings; enum ppm_event_type evt_type; struct sys_stash_args args; unsigned long *argsp; settings = get_bpf_settings(); if (!settings) return 0; if (!settings->capture_enabled) return 0; argsp = __unstash_args(ctx->parent_pid); if (!argsp) return 0; memcpy(&args, argsp, sizeof(args)); __stash_args(ctx->child_pid, args.args); return 0; } #endif char kernel_ver[] __bpf_section("kernel_version") = UTS_RELEASE; char probe_ver[] __bpf_section("probe_version") = PROBE_VERSION; sysdig-0.26.4/driver/bpf/quirks.h000066400000000000000000000017261352731327100166500ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifndef __QUIRKS_H #define __QUIRKS_H #include #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) #error Kernel version must be >= 4.14 with eBPF enabled #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 4) #define randomized_struct_fields_start struct { #define randomized_struct_fields_end }; #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) #define BPF_FORBIDS_ZERO_ACCESS #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) #define BPF_SUPPORTS_RAW_TRACEPOINTS #endif /* Redefine asm_volatile_goto to work around clang not supporting it */ #include #ifdef asm_volatile_goto #undef asm_volatile_goto #define asm_volatile_goto(...) asm volatile("invalid use of asm_volatile_goto") #endif #endif sysdig-0.26.4/driver/bpf/ring_helpers.h000066400000000000000000000051261352731327100200110ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifndef __RING_HELPERS_H #define __RING_HELPERS_H static __always_inline void write_evt_hdr(struct filler_data *data) { struct ppm_evt_hdr *evt_hdr = (struct ppm_evt_hdr *)data->buf; evt_hdr->ts = data->state->tail_ctx.ts; evt_hdr->tid = bpf_get_current_pid_tgid() & 0xffffffff; evt_hdr->type = data->state->tail_ctx.evt_type; evt_hdr->nparams = data->evt->nparams; data->state->tail_ctx.curoff = sizeof(struct ppm_evt_hdr) + sizeof(u16) * data->evt->nparams; data->state->tail_ctx.len = data->state->tail_ctx.curoff; } static __always_inline void fixup_evt_len(char *p, unsigned long len) { struct ppm_evt_hdr *evt_hdr = (struct ppm_evt_hdr *)p; evt_hdr->len = len; } static __always_inline void fixup_evt_arg_len(char *p, unsigned int argnum, unsigned int arglen) { volatile unsigned int argnumv = argnum; *((u16 *)&p[sizeof(struct ppm_evt_hdr)] + (argnumv & (PPM_MAX_EVENT_PARAMS - 1))) = arglen; } static __always_inline int push_evt_frame(void *ctx, struct filler_data *data) { if (data->state->tail_ctx.curarg != data->evt->nparams) { bpf_printk("corrupted filler for event type %d (added %u args, should have added %u)\n", data->state->tail_ctx.evt_type, data->state->tail_ctx.curarg, data->evt->nparams); return PPM_FAILURE_BUG; } if (data->state->tail_ctx.len > PERF_EVENT_MAX_SIZE) return PPM_FAILURE_BUFFER_FULL; fixup_evt_len(data->buf, data->state->tail_ctx.len); #ifdef BPF_FORBIDS_ZERO_ACCESS int res = bpf_perf_event_output(ctx, &perf_map, BPF_F_CURRENT_CPU, data->buf, ((data->state->tail_ctx.len - 1) & SCRATCH_SIZE_MAX) + 1); #else int res = bpf_perf_event_output(ctx, &perf_map, BPF_F_CURRENT_CPU, data->buf, data->state->tail_ctx.len & SCRATCH_SIZE_MAX); #endif if (res == -ENOENT || res == -EOPNOTSUPP) { /* * ENOENT = likely a new CPU is online that wasn't * opened in userspace * * EOPNOTSUPP = likely a perf channel has been closed * because a CPU went offline * * Schedule a hotplug event on CPU 0 */ struct sysdig_bpf_per_cpu_state *state = get_local_state(0); if (!state) return PPM_FAILURE_BUG; state->hotplug_cpu = bpf_get_smp_processor_id(); bpf_printk("detected hotplug event, cpu=%d\n", state->hotplug_cpu); } else if (res) { bpf_printk("bpf_perf_event_output failed, res=%d\n", res); return PPM_FAILURE_BUG; } return PPM_SUCCESS; } #endif sysdig-0.26.4/driver/bpf/types.h000066400000000000000000000106721352731327100164760ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifndef __TYPES_H #define __TYPES_H #ifdef __KERNEL__ #define __bpf_section(NAME) __attribute__((section(NAME), used)) #define __always_inline inline __attribute__((always_inline)) #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS #define TP_NAME "raw_tracepoint/" #else #define TP_NAME "tracepoint/" #endif #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS struct sys_enter_args { unsigned long regs; unsigned long id; }; #else struct sys_enter_args { __u64 pad; long id; unsigned long args[6]; }; #endif #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS struct sys_exit_args { unsigned long regs; unsigned long ret; }; #else struct sys_exit_args { __u64 pad; long id; long ret; }; #endif #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS struct sched_process_exit_args { unsigned long p; }; #else struct sched_process_exit_args { __u64 pad; char comm[16]; pid_t pid; int prio; }; #endif #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS struct sched_switch_args { unsigned long preempt; unsigned long prev; unsigned long next; }; #else struct sched_switch_args { __u64 pad; char prev_comm[TASK_COMM_LEN]; pid_t prev_pid; int prev_prio; long prev_state; char next_comm[TASK_COMM_LEN]; pid_t next_pid; int next_prio; }; #endif #ifndef BPF_SUPPORTS_RAW_TRACEPOINTS struct sched_process_fork_args { __u64 pad; char parent_comm[TASK_COMM_LEN]; pid_t parent_pid; char child_comm[TASK_COMM_LEN]; pid_t child_pid; }; #endif #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS struct page_fault_args { unsigned long address; unsigned long regs; unsigned long error_code; }; #else struct page_fault_args { __u64 pad; unsigned long address; unsigned long ip; unsigned long error_code; }; #endif #ifdef BPF_SUPPORTS_RAW_TRACEPOINTS struct signal_deliver_args { unsigned long sig; unsigned long info; unsigned long ka; }; #else struct signal_deliver_args { __u64 pad; int sig; int errno; int code; unsigned long sa_handler; unsigned long sa_flags; }; #endif #ifndef BPF_SUPPORTS_RAW_TRACEPOINTS struct sys_stash_args { unsigned long args[6]; }; #endif struct filler_data { void *ctx; struct sysdig_bpf_settings *settings; struct sysdig_bpf_per_cpu_state *state; char *tmp_scratch; const struct ppm_event_info *evt; const struct ppm_event_entry *filler_info; bool curarg_already_on_frame; char *buf; #ifndef BPF_SUPPORTS_RAW_TRACEPOINTS unsigned long *args; #endif int fd; }; struct perf_event_header { __u32 type; __u16 misc; __u16 size; }; struct perf_event_sample { struct perf_event_header header; __u32 size; char data[]; }; /* * Unfortunately the entire perf event length must fit in u16 */ #define PERF_EVENT_MAX_SIZE (0xffff - sizeof(struct perf_event_sample)) /* * Due to the way the verifier works with accessing variable memory, * the scratch size needs to be at least 2^N > PERF_EVENT_MAX_SIZE * 2 */ #define SCRATCH_SIZE (1 << 18) #define SCRATCH_SIZE_MAX (SCRATCH_SIZE - 1) #define SCRATCH_SIZE_HALF (SCRATCH_SIZE_MAX >> 1) #endif /* __KERNEL__ */ struct bpf_map_def { unsigned int type; unsigned int key_size; unsigned int value_size; unsigned int max_entries; unsigned int map_flags; unsigned int inner_map_idx; unsigned int numa_node; }; enum sysdig_map_types { SYSDIG_PERF_MAP = 0, SYSDIG_TAIL_MAP = 1, SYSDIG_SYSCALL_CODE_ROUTING_TABLE = 2, SYSDIG_SYSCALL_TABLE = 3, SYSDIG_EVENT_INFO_TABLE = 4, SYSDIG_FILLERS_TABLE = 5, SYSDIG_FRAME_SCRATCH_MAP = 6, SYSDIG_TMP_SCRATCH_MAP = 7, SYSDIG_SETTINGS_MAP = 8, SYSDIG_LOCAL_STATE_MAP = 9, #ifndef BPF_SUPPORTS_RAW_TRACEPOINTS SYSDIG_STASH_MAP = 10, #endif }; struct sysdig_bpf_settings { uint64_t boot_time; void *socket_file_ops; uint32_t snaplen; uint32_t sampling_ratio; bool capture_enabled; bool do_dynamic_snaplen; bool page_faults; bool dropping_mode; bool is_dropping; bool tracers_enabled; uint16_t fullcapture_port_range_start; uint16_t fullcapture_port_range_end; uint16_t statsd_port; } __attribute__((packed)); struct tail_context { enum ppm_event_type evt_type; unsigned long long ts; unsigned long curarg; unsigned long curoff; unsigned long len; int prev_res; } __attribute__((packed)); struct sysdig_bpf_per_cpu_state { struct tail_context tail_ctx; unsigned long long n_evts; unsigned long long n_drops_buffer; unsigned long long n_drops_pf; unsigned long long n_drops_bug; unsigned int hotplug_cpu; bool in_use; } __attribute__((packed)); #endif sysdig-0.26.4/driver/dkms.conf.in000066400000000000000000000002361352731327100166170ustar00rootroot00000000000000PACKAGE_NAME="@PACKAGE_NAME@" PACKAGE_VERSION="@PROBE_VERSION@" BUILT_MODULE_NAME[0]="@PROBE_NAME@" DEST_MODULE_LOCATION[0]="/kernel/extra" AUTOINSTALL="yes" sysdig-0.26.4/driver/driver_config.h.in000066400000000000000000000004701352731327100200030ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #pragma once #define PROBE_VERSION "${PROBE_VERSION}" #define PROBE_NAME "${PROBE_NAME}" #define PROBE_DEVICE_NAME "${PROBE_DEVICE_NAME}" sysdig-0.26.4/driver/dynamic_params_table.c000066400000000000000000000016021352731327100207050ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #include "ppm_events_public.h" const struct ppm_param_info sockopt_dynamic_param[PPM_SOCKOPT_IDX_MAX] = { [PPM_SOCKOPT_IDX_UNKNOWN] = {{0}, PT_BYTEBUF, PF_HEX}, [PPM_SOCKOPT_IDX_ERRNO] = {{0}, PT_ERRNO, PF_DEC}, [PPM_SOCKOPT_IDX_UINT32] = {{0}, PT_UINT32, PF_DEC}, [PPM_SOCKOPT_IDX_UINT64] = {{0}, PT_UINT64, PF_DEC}, [PPM_SOCKOPT_IDX_TIMEVAL] = {{0}, PT_RELTIME, PF_DEC}, }; const struct ppm_param_info ptrace_dynamic_param[PPM_PTRACE_IDX_MAX] = { [PPM_PTRACE_IDX_UINT64] = {{0}, PT_UINT64, PF_HEX}, [PPM_PTRACE_IDX_SIGTYPE] = {{0}, PT_SIGTYPE, PF_DEC}, }; const struct ppm_param_info bpf_dynamic_param[PPM_BPF_IDX_MAX] = { [PPM_BPF_IDX_FD] = {{0}, PT_FD, PF_DEC}, [PPM_BPF_IDX_RES] = {{0}, PT_ERRNO, PF_DEC}, }; sysdig-0.26.4/driver/event_table.c000066400000000000000000001420271352731327100170460ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #include "ppm_events_public.h" const struct ppm_event_info g_event_info[PPM_EVENT_MAX] = { /* PPME_GENERIC_E */{"syscall", EC_OTHER, EF_NONE, 2, {{"ID", PT_SYSCALLID, PF_DEC}, {"nativeID", PT_UINT16, PF_DEC} } }, /* PPME_GENERIC_X */{"syscall", EC_OTHER, EF_NONE, 1, {{"ID", PT_SYSCALLID, PF_DEC} } }, /* PPME_SYSCALL_OPEN_E */{"open", EC_FILE, EF_CREATES_FD | EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_OPEN_X */{"open", EC_FILE, EF_CREATES_FD | EF_MODIFIES_STATE, 5, {{"fd", PT_FD, PF_DEC}, {"name", PT_FSPATH, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, file_flags}, {"mode", PT_UINT32, PF_OCT}, {"dev", PT_UINT32, PF_HEX} } }, /* PPME_SYSCALL_CLOSE_E */{"close", EC_IO_OTHER, EF_DESTROYS_FD | EF_USES_FD | EF_MODIFIES_STATE | EF_DROP_FALCO, 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_CLOSE_X */{"close", EC_IO_OTHER, EF_DESTROYS_FD | EF_USES_FD | EF_MODIFIES_STATE | EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_READ_E */{"read", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_FALCO, 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_READ_X */{"read", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_WRITE_E */{"write", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_DROP_FALCO, 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_WRITE_X */{"write", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_BRK_1_E */{"brk", EC_MEMORY, EF_OLD_VERSION, 1, {{"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_BRK_1_X */{"brk", EC_MEMORY, EF_OLD_VERSION, 1, {{"res", PT_UINT64, PF_HEX} } }, /* PPME_SYSCALL_EXECVE_8_E */{"execve", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_SYSCALL_EXECVE_8_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 8, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC} } }, /* PPME_CLONE_11_E */{"clone", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_CLONE_11_X */{"clone", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 11, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC} } }, /* PPME_PROCEXIT_E */{"procexit", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_NA1 */{"NA1", EC_PROCESS, EF_UNUSED, 0}, /* PPME_SOCKET_SOCKET_E */{"socket", EC_NET, EF_CREATES_FD | EF_MODIFIES_STATE, 3, {{"domain", PT_FLAGS32, PF_DEC, socket_families}, {"type", PT_UINT32, PF_DEC}, {"proto", PT_UINT32, PF_DEC} } }, /* PPME_SOCKET_SOCKET_X */{"socket", EC_NET, EF_CREATES_FD | EF_MODIFIES_STATE, 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SOCKET_BIND_E */{"bind", EC_NET, EF_USES_FD | EF_MODIFIES_STATE, 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SOCKET_BIND_X */{"bind", EC_NET, EF_USES_FD | EF_MODIFIES_STATE, 2, {{"res", PT_ERRNO, PF_DEC}, {"addr", PT_SOCKADDR, PF_NA} } }, /* PPME_SOCKET_CONNECT_E */{"connect", EC_NET, EF_USES_FD | EF_MODIFIES_STATE, 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SOCKET_CONNECT_X */{"connect", EC_NET, EF_USES_FD | EF_MODIFIES_STATE, 2, {{"res", PT_ERRNO, PF_DEC}, {"tuple", PT_SOCKTUPLE, PF_NA} } }, /* PPME_SOCKET_LISTEN_E */{"listen", EC_NET, EF_USES_FD, 2, {{"fd", PT_FD, PF_DEC}, {"backlog", PT_UINT32, PF_DEC} } }, /* PPME_SOCKET_LISTEN_X */{"listen", EC_NET, EF_USES_FD, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SOCKET_ACCEPT_E */{"accept", EC_NET, EF_CREATES_FD | EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_SOCKET_ACCEPT_X */{"accept", EC_NET, EF_CREATES_FD | EF_MODIFIES_STATE | EF_OLD_VERSION, 3, {{"fd", PT_FD, PF_DEC}, {"tuple", PT_SOCKTUPLE, PF_NA}, {"queuepct", PT_UINT8, PF_DEC} } }, /* PPME_SYSCALL_SEND_E */{"send", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_DROP_FALCO, 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_SEND_X */{"send", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SOCKET_SENDTO_E */{"sendto", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_MODIFIES_STATE, 3, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"tuple", PT_SOCKTUPLE, PF_NA} } }, /* PPME_SOCKET_SENDTO_X */{"sendto", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_MODIFIES_STATE, 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SOCKET_RECV_E */{"recv", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_FALCO, 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SOCKET_RECV_X */{"recv", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SOCKET_RECVFROM_E */{"recvfrom", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE, 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SOCKET_RECVFROM_X */{"recvfrom", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE, 3, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA}, {"tuple", PT_SOCKTUPLE, PF_NA} } }, /* PPME_SOCKET_SHUTDOWN_E */{"shutdown", EC_NET, EF_USES_FD | EF_MODIFIES_STATE | EF_DROP_FALCO, 2, {{"fd", PT_FD, PF_DEC}, {"how", PT_FLAGS8, PF_HEX, shutdown_how} } }, /* PPME_SOCKET_SHUTDOWN_X */{"shutdown", EC_NET, EF_USES_FD | EF_MODIFIES_STATE | EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SOCKET_GETSOCKNAME_E */{"getsockname", EC_NET, EF_DROP_FALCO, 0}, /* PPME_SOCKET_GETSOCKNAME_X */{"getsockname", EC_NET, EF_DROP_FALCO, 0}, /* PPME_SOCKET_GETPEERNAME_E */{"getpeername", EC_NET, EF_DROP_FALCO, 0}, /* PPME_SOCKET_GETPEERNAME_X */{"getpeername", EC_NET, EF_DROP_FALCO, 0}, /* PPME_SOCKET_SOCKETPAIR_E */{"socketpair", EC_IPC, EF_CREATES_FD | EF_MODIFIES_STATE, 3, {{"domain", PT_FLAGS32, PF_DEC, socket_families}, {"type", PT_UINT32, PF_DEC}, {"proto", PT_UINT32, PF_DEC} } }, /* PPME_SOCKET_SOCKETPAIR_X */{"socketpair", EC_IPC, EF_CREATES_FD | EF_MODIFIES_STATE, 5, {{"res", PT_ERRNO, PF_DEC}, {"fd1", PT_FD, PF_DEC}, {"fd2", PT_FD, PF_DEC}, {"source", PT_UINT64, PF_HEX}, {"peer", PT_UINT64, PF_HEX} } }, /* PPME_SOCKET_SETSOCKOPT_E */{"setsockopt", EC_NET, EF_NONE, 0 }, /* PPME_SOCKET_SETSOCKOPT_X */{"setsockopt", EC_NET, EF_USES_FD, 6, {{"res", PT_ERRNO, PF_DEC}, {"fd", PT_FD, PF_DEC}, {"level", PT_FLAGS8, PF_DEC, sockopt_levels}, {"optname", PT_FLAGS8, PF_DEC, sockopt_options}, {"val", PT_DYN, PF_DEC, sockopt_dynamic_param, PPM_SOCKOPT_IDX_MAX}, {"optlen", PT_UINT32, PF_DEC}}}, /* PPME_SOCKET_GETSOCKOPT_E */{"getsockopt", EC_NET, EF_MODIFIES_STATE | EF_DROP_FALCO, 0 }, /* PPME_SOCKET_GETSOCKOPT_X */{"getsockopt", EC_NET, EF_USES_FD | EF_MODIFIES_STATE| EF_DROP_FALCO, 6, {{"res", PT_ERRNO, PF_DEC}, {"fd", PT_FD, PF_DEC}, {"level", PT_FLAGS8, PF_DEC, sockopt_levels}, {"optname", PT_FLAGS8, PF_DEC, sockopt_options}, {"val", PT_DYN, PF_DEC, sockopt_dynamic_param, PPM_SOCKOPT_IDX_MAX}, {"optlen", PT_UINT32, PF_DEC}}}, /* PPME_SOCKET_SENDMSG_E */{"sendmsg", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_MODIFIES_STATE, 3, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"tuple", PT_SOCKTUPLE, PF_NA} } }, /* PPME_SOCKET_SENDMSG_X */{"sendmsg", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_MODIFIES_STATE, 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SOCKET_SENDMMSG_E */{"sendmmsg", EC_IO_WRITE, EF_DROP_FALCO, 0}, /* PPME_SOCKET_SENDMMSG_X */{"sendmmsg", EC_IO_WRITE, EF_DROP_FALCO, 0}, /* PPME_SOCKET_RECVMSG_E */{"recvmsg", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE, 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SOCKET_RECVMSG_X */{"recvmsg", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE, 4, {{"res", PT_ERRNO, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"data", PT_BYTEBUF, PF_NA}, {"tuple", PT_SOCKTUPLE, PF_NA} } }, /* PPME_SOCKET_RECVMMSG_E */{"recvmmsg", EC_IO_READ, EF_DROP_FALCO, 0}, /* PPME_SOCKET_RECVMMSG_X */{"recvmmsg", EC_IO_READ, EF_DROP_FALCO, 0}, /* PPME_SOCKET_ACCEPT4_E */{"accept", EC_NET, EF_CREATES_FD | EF_MODIFIES_STATE | EF_OLD_VERSION, 1, {{"flags", PT_INT32, PF_HEX} } }, /* PPME_SOCKET_ACCEPT4_X */{"accept", EC_NET, EF_CREATES_FD | EF_MODIFIES_STATE | EF_OLD_VERSION, 3, {{"fd", PT_FD, PF_DEC}, {"tuple", PT_SOCKTUPLE, PF_NA}, {"queuepct", PT_UINT8, PF_DEC} } }, /* PPME_SYSCALL_CREAT_E */{"creat", EC_FILE, EF_CREATES_FD | EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_CREAT_X */{"creat", EC_FILE, EF_CREATES_FD | EF_MODIFIES_STATE, 4, {{"fd", PT_FD, PF_DEC}, {"name", PT_FSPATH, PF_NA}, {"mode", PT_UINT32, PF_OCT}, {"dev", PT_UINT32, PF_HEX} } }, /* PPME_SOCKET_PIPE_E */{"pipe", EC_IPC, EF_CREATES_FD | EF_MODIFIES_STATE, 0}, /* PPME_SOCKET_PIPE_X */{"pipe", EC_IPC, EF_CREATES_FD | EF_MODIFIES_STATE, 4, {{"res", PT_ERRNO, PF_DEC}, {"fd1", PT_FD, PF_DEC}, {"fd2", PT_FD, PF_DEC}, {"ino", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_EVENTFD_E */{"eventfd", EC_IPC, EF_CREATES_FD | EF_MODIFIES_STATE | EF_DROP_FALCO, 2, {{"initval", PT_UINT64, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX} } }, /* PPME_SYSCALL_EVENTFD_X */{"eventfd", EC_IPC, EF_CREATES_FD | EF_MODIFIES_STATE | EF_DROP_FALCO, 1, {{"res", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_FUTEX_E */{"futex", EC_IPC, EF_DROP_FALCO, 3, {{"addr", PT_UINT64, PF_HEX}, {"op", PT_FLAGS16, PF_HEX, futex_operations}, {"val", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_FUTEX_X */{"futex", EC_IPC, EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_STAT_E */{"stat", EC_FILE, EF_DROP_FALCO, 0}, /* PPME_SYSCALL_STAT_X */{"stat", EC_FILE, EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_LSTAT_E */{"lstat", EC_FILE, EF_DROP_FALCO, 0}, /* PPME_SYSCALL_LSTAT_X */{"lstat", EC_FILE, EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_FSTAT_E */{"fstat", EC_FILE, EF_USES_FD | EF_DROP_FALCO, 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_FSTAT_X */{"fstat", EC_FILE, EF_USES_FD | EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_STAT64_E */{"stat64", EC_FILE, EF_DROP_FALCO, 0}, /* PPME_SYSCALL_STAT64_X */{"stat64", EC_FILE, EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_LSTAT64_E */{"lstat64", EC_FILE, EF_DROP_FALCO, 0}, /* PPME_SYSCALL_LSTAT64_X */{"lstat64", EC_FILE, EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_FSTAT64_E */{"fstat64", EC_FILE, EF_USES_FD | EF_DROP_FALCO, 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_FSTAT64_X */{"fstat64", EC_FILE, EF_USES_FD | EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_EPOLLWAIT_E */{"epoll_wait", EC_WAIT, EF_WAITS | EF_DROP_FALCO, 1, {{"maxevents", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_EPOLLWAIT_X */{"epoll_wait", EC_WAIT, EF_WAITS | EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_POLL_E */{"poll", EC_WAIT, EF_WAITS | EF_DROP_FALCO, 2, {{"fds", PT_FDLIST, PF_DEC}, {"timeout", PT_INT64, PF_DEC} } }, /* PPME_SYSCALL_POLL_X */{"poll", EC_WAIT, EF_WAITS | EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"fds", PT_FDLIST, PF_DEC} } }, /* PPME_SYSCALL_SELECT_E */{"select", EC_WAIT, EF_WAITS | EF_DROP_FALCO, 0}, /* PPME_SYSCALL_SELECT_X */{"select", EC_WAIT, EF_WAITS | EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_NEWSELECT_E */{"select", EC_WAIT, EF_WAITS | EF_DROP_FALCO, 0}, /* PPME_SYSCALL_NEWSELECT_X */{"select", EC_WAIT, EF_WAITS | EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_LSEEK_E */{"lseek", EC_FILE, EF_USES_FD | EF_DROP_FALCO, 3, {{"fd", PT_FD, PF_DEC}, {"offset", PT_UINT64, PF_DEC}, {"whence", PT_FLAGS8, PF_DEC, lseek_whence} } }, /* PPME_SYSCALL_LSEEK_X */{"lseek", EC_FILE, EF_USES_FD | EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_LLSEEK_E */{"llseek", EC_FILE, EF_USES_FD | EF_DROP_FALCO, 3, {{"fd", PT_FD, PF_DEC}, {"offset", PT_UINT64, PF_DEC}, {"whence", PT_FLAGS8, PF_DEC, lseek_whence} } }, /* PPME_SYSCALL_LLSEEK_X */{"llseek", EC_FILE, EF_USES_FD | EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_IOCTL_2_E */{"ioctl", EC_IO_OTHER, EF_USES_FD | EF_OLD_VERSION, 2, {{"fd", PT_FD, PF_DEC}, {"request", PT_UINT64, PF_HEX} } }, /* PPME_SYSCALL_IOCTL_2_X */{"ioctl", EC_IO_OTHER, EF_USES_FD | EF_OLD_VERSION, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_GETCWD_E */{"getcwd", EC_FILE, EF_DROP_FALCO, 0}, /* Note: path is PT_CHARBUF and not PT_FSPATH because we assume it's abosulte and will never need resolution */ /* PPME_SYSCALL_GETCWD_X */{"getcwd", EC_FILE, EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_CHARBUF, PF_NA} } }, /* Note: path is PT_CHARBUF and not PT_FSPATH because we don't want it to be resolved, since the event handler already changes it */ /* PPME_SYSCALL_CHDIR_E */{"chdir", EC_FILE, EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_CHDIR_X */{"chdir", EC_FILE, EF_MODIFIES_STATE, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_CHARBUF, PF_NA} } }, /* PPME_SYSCALL_FCHDIR_E */{"fchdir", EC_FILE, EF_USES_FD | EF_MODIFIES_STATE, 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_FCHDIR_X */{"fchdir", EC_FILE, EF_USES_FD | EF_MODIFIES_STATE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_MKDIR_E */{"mkdir", EC_FILE, EF_NONE, 2, {{"path", PT_FSPATH, PF_NA}, {"mode", PT_UINT32, PF_HEX} } }, /* PPME_SYSCALL_MKDIR_X */{"mkdir", EC_FILE, EF_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_RMDIR_E */{"rmdir", EC_FILE, EF_NONE, 1, {{"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_RMDIR_X */{"rmdir", EC_FILE, EF_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_OPENAT_E */{"openat", EC_FILE, EF_CREATES_FD | EF_MODIFIES_STATE | EF_OLD_VERSION, 4, {{"dirfd", PT_FD, PF_DEC}, {"name", PT_CHARBUF, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, file_flags}, {"mode", PT_UINT32, PF_OCT} } }, /* PPME_SYSCALL_OPENAT_X */{"openat", EC_FILE, EF_CREATES_FD | EF_MODIFIES_STATE | EF_OLD_VERSION, 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_LINK_E */{"link", EC_FILE, EF_OLD_VERSION, 2, {{"oldpath", PT_FSPATH, PF_NA}, {"newpath", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_LINK_X */{"link", EC_FILE, EF_OLD_VERSION, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_LINKAT_E */{"linkat", EC_FILE, EF_OLD_VERSION, 4, {{"olddir", PT_FD, PF_DEC}, {"oldpath", PT_CHARBUF, PF_NA}, {"newdir", PT_FD, PF_DEC}, {"newpath", PT_CHARBUF, PF_NA} } }, /* PPME_SYSCALL_LINKAT_X */{"linkat", EC_FILE, EF_OLD_VERSION, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_UNLINK_E */{"unlink", EC_FILE, EF_OLD_VERSION, 1, {{"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_UNLINK_X */{"unlink", EC_FILE, EF_OLD_VERSION, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_UNLINKAT_E */{"unlinkat", EC_FILE, EF_OLD_VERSION, 2, {{"dirfd", PT_FD, PF_DEC}, {"name", PT_CHARBUF, PF_NA} } }, /* PPME_SYSCALL_UNLINKAT_X */{"unlinkat", EC_FILE, EF_OLD_VERSION, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_PREAD_E */{"pread", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_FALCO, 3, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"pos", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_PREAD_X */{"pread", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_PWRITE_E */{"pwrite", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_DROP_FALCO, 3, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"pos", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_PWRITE_X */{"pwrite", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_READV_E */{"readv", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_FALCO, 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_READV_X */{"readv", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_FALCO, 3, {{"res", PT_ERRNO, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_WRITEV_E */{"writev", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_DROP_FALCO, 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_WRITEV_X */{"writev", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_PREADV_E */{"preadv", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_FALCO, 2, {{"fd", PT_FD, PF_DEC}, {"pos", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_PREADV_X */{"preadv", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_DROP_FALCO, 3, {{"res", PT_ERRNO, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_PWRITEV_E */{"pwritev", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_DROP_FALCO, 3, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"pos", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_PWRITEV_X */{"pwritev", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_DUP_E */{"dup", EC_IO_OTHER, EF_CREATES_FD | EF_USES_FD | EF_MODIFIES_STATE, 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_DUP_X */{"dup", EC_IO_OTHER, EF_CREATES_FD | EF_USES_FD | EF_MODIFIES_STATE, 1, {{"res", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_SIGNALFD_E */{"signalfd", EC_SIGNAL, EF_CREATES_FD | EF_MODIFIES_STATE, 3, {{"fd", PT_FD, PF_DEC}, {"mask", PT_UINT32, PF_HEX}, {"flags", PT_FLAGS8, PF_HEX} } }, /* PPME_SYSCALL_SIGNALFD_X */{"signalfd", EC_SIGNAL, EF_CREATES_FD | EF_MODIFIES_STATE, 1, {{"res", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_KILL_E */{"kill", EC_SIGNAL, EF_NONE, 2, {{"pid", PT_PID, PF_DEC}, {"sig", PT_SIGTYPE, PF_DEC} } }, /* PPME_SYSCALL_KILL_X */{"kill", EC_SIGNAL, EF_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_TKILL_E */{"tkill", EC_SIGNAL, EF_NONE, 2, {{"tid", PT_PID, PF_DEC}, {"sig", PT_SIGTYPE, PF_DEC} } }, /* PPME_SYSCALL_TKILL_X */{"tkill", EC_SIGNAL, EF_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_TGKILL_E */{"tgkill", EC_SIGNAL, EF_NONE, 3, {{"pid", PT_PID, PF_DEC}, {"tid", PT_PID, PF_DEC}, {"sig", PT_SIGTYPE, PF_DEC} } }, /* PPME_SYSCALL_TGKILL_X */{"tgkill", EC_SIGNAL, EF_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_NANOSLEEP_E */{"nanosleep", EC_SLEEP, EF_WAITS | EF_DROP_FALCO, 1, {{"interval", PT_RELTIME, PF_DEC} } }, /* PPME_SYSCALL_NANOSLEEP_X */{"nanosleep", EC_SLEEP, EF_WAITS | EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_TIMERFD_CREATE_E */{"timerfd_create", EC_TIME, EF_CREATES_FD | EF_MODIFIES_STATE | EF_DROP_FALCO, 2, {{"clockid", PT_UINT8, PF_DEC}, {"flags", PT_FLAGS8, PF_HEX} } }, /* PPME_SYSCALL_TIMERFD_CREATE_X */{"timerfd_create", EC_TIME, EF_CREATES_FD | EF_MODIFIES_STATE | EF_DROP_FALCO, 1, {{"res", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_INOTIFY_INIT_E */{"inotify_init", EC_IPC, EF_CREATES_FD | EF_MODIFIES_STATE, 1, {{"flags", PT_FLAGS8, PF_HEX} } }, /* PPME_SYSCALL_INOTIFY_INIT_X */{"inotify_init", EC_IPC, EF_CREATES_FD | EF_MODIFIES_STATE, 1, {{"res", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_GETRLIMIT_E */{"getrlimit", EC_PROCESS, EF_DROP_FALCO, 1, {{"resource", PT_FLAGS8, PF_DEC, rlimit_resources} } }, /* PPME_SYSCALL_GETRLIMIT_X */{"getrlimit", EC_PROCESS, EF_DROP_FALCO, 3, {{"res", PT_ERRNO, PF_DEC}, {"cur", PT_INT64, PF_DEC}, {"max", PT_INT64, PF_DEC} } }, /* PPME_SYSCALL_SETRLIMIT_E */{"setrlimit", EC_PROCESS, EF_DROP_FALCO, 1, {{"resource", PT_FLAGS8, PF_DEC, rlimit_resources} } }, /* PPME_SYSCALL_SETRLIMIT_X */{"setrlimit", EC_PROCESS, EF_DROP_FALCO, 3, {{"res", PT_ERRNO, PF_DEC}, {"cur", PT_INT64, PF_DEC}, {"max", PT_INT64, PF_DEC} } }, /* PPME_SYSCALL_PRLIMIT_E */{"prlimit", EC_PROCESS, EF_NONE, 2, {{"pid", PT_PID, PF_DEC}, {"resource", PT_FLAGS8, PF_DEC, rlimit_resources} } }, /* PPME_SYSCALL_PRLIMIT_X */{"prlimit", EC_PROCESS, EF_NONE, 5, {{"res", PT_ERRNO, PF_DEC}, {"newcur", PT_INT64, PF_DEC}, {"newmax", PT_INT64, PF_DEC}, {"oldcur", PT_INT64, PF_DEC}, {"oldmax", PT_INT64, PF_DEC} } }, /* PPME_SCHEDSWITCH_1_E */{"switch", EC_SCHEDULER, EF_SKIPPARSERESET | EF_OLD_VERSION | EF_DROP_FALCO, 1, {{"next", PT_PID, PF_DEC} } }, /* PPME_SCHEDSWITCH_1_X */{"NA2", EC_SCHEDULER, EF_SKIPPARSERESET | EF_UNUSED | EF_OLD_VERSION, 0}, /* PPME_DROP_E */{"drop", EC_INTERNAL, EF_SKIPPARSERESET, 1, {{"ratio", PT_UINT32, PF_DEC} } }, /* PPME_DROP_X */{"drop", EC_INTERNAL, EF_SKIPPARSERESET, 1, {{"ratio", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_FCNTL_E */{"fcntl", EC_IO_OTHER, EF_USES_FD | EF_MODIFIES_STATE | EF_DROP_FALCO, 2, {{"fd", PT_FD, PF_DEC}, {"cmd", PT_FLAGS8, PF_DEC, fcntl_commands} } }, /* PPME_SYSCALL_FCNTL_X */{"fcntl", EC_IO_OTHER, EF_USES_FD | EF_MODIFIES_STATE | EF_DROP_FALCO, 1, {{"res", PT_FD, PF_DEC} } }, /* PPME_SCHEDSWITCH_6_E */{"switch", EC_SCHEDULER, EF_DROP_FALCO, 6, {{"next", PT_PID, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC} } }, /* PPME_SCHEDSWITCH_6_X */{"NA2", EC_SCHEDULER, EF_UNUSED, 0}, /* PPME_SYSCALL_EXECVE_13_E */{"execve", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_SYSCALL_EXECVE_13_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 13, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC} } }, /* PPME_CLONE_16_E */{"clone", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_CLONE_16_X */{"clone", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 16, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_BRK_4_E */{"brk", EC_MEMORY, EF_DROP_FALCO, 1, {{"addr", PT_UINT64, PF_HEX} } }, /* PPME_SYSCALL_BRK_4_X */{"brk", EC_MEMORY, EF_DROP_FALCO, 4, {{"res", PT_UINT64, PF_HEX}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_MMAP_E */{"mmap", EC_MEMORY, EF_DROP_FALCO, 6, {{"addr", PT_UINT64, PF_HEX}, {"length", PT_UINT64, PF_DEC}, {"prot", PT_FLAGS32, PF_HEX, prot_flags}, {"flags", PT_FLAGS32, PF_HEX, mmap_flags}, {"fd", PT_FD, PF_DEC}, {"offset", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_MMAP_X */{"mmap", EC_MEMORY, EF_DROP_FALCO, 4, {{"res", PT_UINT64, PF_HEX}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_MMAP2_E */{"mmap2", EC_MEMORY, EF_DROP_FALCO, 6, {{"addr", PT_UINT64, PF_HEX}, {"length", PT_UINT64, PF_DEC}, {"prot", PT_FLAGS32, PF_HEX, prot_flags}, {"flags", PT_FLAGS32, PF_HEX, mmap_flags}, {"fd", PT_FD, PF_DEC}, {"pgoffset", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_MMAP2_X */{"mmap2", EC_MEMORY, EF_DROP_FALCO, 4, {{"res", PT_UINT64, PF_HEX}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_MUNMAP_E */{"munmap", EC_MEMORY, EF_DROP_FALCO, 2, {{"addr", PT_UINT64, PF_HEX}, {"length", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_MUNMAP_X */{"munmap", EC_MEMORY, EF_DROP_FALCO, 4, {{"res", PT_ERRNO, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_SPLICE_E */{"splice", EC_IO_OTHER, EF_USES_FD | EF_DROP_FALCO, 4, {{"fd_in", PT_FD, PF_DEC}, {"fd_out", PT_FD, PF_DEC}, {"size", PT_UINT64, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX, splice_flags} } }, /* PPME_SYSCALL_SPLICE_X */{"splice", EC_IO_OTHER, EF_USES_FD | EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_PTRACE_E */{"ptrace", EC_PROCESS, EF_NONE, 2, {{"request", PT_FLAGS16, PF_DEC, ptrace_requests}, {"pid", PT_PID, PF_DEC} } }, /* PPME_SYSCALL_PTRACE_X */{"ptrace", EC_PROCESS, EF_NONE, 3, {{"res", PT_ERRNO, PF_DEC}, {"addr", PT_DYN, PF_HEX, ptrace_dynamic_param, PPM_PTRACE_IDX_MAX}, {"data", PT_DYN, PF_HEX, ptrace_dynamic_param, PPM_PTRACE_IDX_MAX} } }, /* PPME_SYSCALL_IOCTL_3_E */{"ioctl", EC_IO_OTHER, EF_USES_FD, 3, {{"fd", PT_FD, PF_DEC}, {"request", PT_UINT64, PF_HEX}, {"argument", PT_UINT64, PF_HEX} } }, /* PPME_SYSCALL_IOCTL_3_X */{"ioctl", EC_IO_OTHER, EF_USES_FD, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_EXECVE_14_E */{"execve", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_SYSCALL_EXECVE_14_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 14, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"env", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_RENAME_E */{"rename", EC_FILE, EF_NONE, 0 }, /* PPME_SYSCALL_RENAME_X */{"rename", EC_FILE, EF_NONE, 3, {{"res", PT_ERRNO, PF_DEC}, {"oldpath", PT_FSPATH, PF_NA}, {"newpath", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_RENAMEAT_E */{"renameat", EC_FILE, EF_NONE, 0 }, /* PPME_SYSCALL_RENAMEAT_X */{"renameat", EC_FILE, EF_NONE, 5, {{"res", PT_ERRNO, PF_DEC}, {"olddirfd", PT_FD, PF_DEC}, {"oldpath", PT_CHARBUF, PF_NA}, {"newdirfd", PT_FD, PF_DEC}, {"newpath", PT_CHARBUF, PF_NA} } }, /* PPME_SYSCALL_SYMLINK_E */{"symlink", EC_FILE, EF_NONE, 0 }, /* PPME_SYSCALL_SYMLINK_X */{"symlink", EC_FILE, EF_NONE, 3, {{"res", PT_ERRNO, PF_DEC}, {"target", PT_CHARBUF, PF_NA}, {"linkpath", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_SYMLINKAT_E */{"symlinkat", EC_FILE, EF_NONE, 0 }, /* PPME_SYSCALL_SYMLINKAT_X */{"symlinkat", EC_FILE, EF_NONE, 4, {{"res", PT_ERRNO, PF_DEC}, {"target", PT_CHARBUF, PF_NA}, {"linkdirfd", PT_FD, PF_DEC}, {"linkpath", PT_CHARBUF, PF_NA} } }, /* PPME_SYSCALL_FORK_E */{"fork", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_SYSCALL_FORK_X */{"fork", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 16, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_VFORK_E */{"vfork", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_SYSCALL_VFORK_X */{"vfork", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 16, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC} } }, /* PPME_PROCEXIT_1_E */{"procexit", EC_PROCESS, EF_MODIFIES_STATE, 1, {{"status", PT_ERRNO, PF_DEC} } }, /* PPME_NA1 */{"NA1", EC_PROCESS, EF_UNUSED, 0}, /* PPME_SYSCALL_SENDFILE_E */{"sendfile", EC_IO_WRITE, EF_USES_FD | EF_DROP_FALCO, 4, {{"out_fd", PT_FD, PF_DEC}, {"in_fd", PT_FD, PF_DEC}, {"offset", PT_UINT64, PF_DEC}, {"size", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_SENDFILE_X */{"sendfile", EC_IO_WRITE, EF_USES_FD | EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"offset", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_QUOTACTL_E */{"quotactl", EC_USER, EF_NONE, 4, {{"cmd", PT_FLAGS16, PF_DEC, quotactl_cmds }, {"type", PT_FLAGS8, PF_DEC, quotactl_types}, {"id", PT_UINT32, PF_DEC}, {"quota_fmt", PT_FLAGS8, PF_DEC, quotactl_quota_fmts } } }, /* PPME_SYSCALL_QUOTACTL_X */{"quotactl", EC_USER, EF_NONE, 14, {{"res", PT_ERRNO, PF_DEC}, {"special", PT_CHARBUF, PF_NA }, {"quotafilepath", PT_CHARBUF, PF_NA}, {"dqb_bhardlimit", PT_UINT64, PF_DEC }, {"dqb_bsoftlimit", PT_UINT64, PF_DEC }, {"dqb_curspace", PT_UINT64, PF_DEC }, {"dqb_ihardlimit", PT_UINT64, PF_DEC }, {"dqb_isoftlimit", PT_UINT64, PF_DEC }, {"dqb_btime", PT_RELTIME, PF_DEC }, {"dqb_itime", PT_RELTIME, PF_DEC }, {"dqi_bgrace", PT_RELTIME, PF_DEC }, {"dqi_igrace", PT_RELTIME, PF_DEC }, {"dqi_flags", PT_FLAGS8, PF_DEC, quotactl_dqi_flags }, {"quota_fmt_out", PT_FLAGS8, PF_DEC, quotactl_quota_fmts } } }, /* PPME_SYSCALL_SETRESUID_E */ {"setresuid", EC_USER, EF_MODIFIES_STATE, 3, {{"ruid", PT_UID, PF_DEC }, {"euid", PT_UID, PF_DEC }, {"suid", PT_UID, PF_DEC } } }, /* PPME_SYSCALL_SETRESUID_X */ {"setresuid", EC_USER, EF_MODIFIES_STATE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_SETRESGID_E */ {"setresgid", EC_USER, EF_MODIFIES_STATE, 3, {{"rgid", PT_GID, PF_DEC }, {"egid", PT_GID, PF_DEC }, {"sgid", PT_GID, PF_DEC } } }, /* PPME_SYSCALL_SETRESGID_X */ {"setresgid", EC_USER, EF_MODIFIES_STATE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSDIGEVENT_E */{"sysdigevent", EC_INTERNAL, EF_SKIPPARSERESET, 2, {{"event_type", PT_UINT32, PF_DEC}, {"event_data", PT_UINT64, PF_DEC} } }, /* PPME_NA1 */{"sysdigevent", EC_INTERNAL, EF_UNUSED, 0}, /* PPME_SYSCALL_SETUID_E */ {"setuid", EC_USER, EF_MODIFIES_STATE, 1, {{"uid", PT_UID, PF_DEC} } }, /* PPME_SYSCALL_SETUID_X */ {"setuid", EC_USER, EF_MODIFIES_STATE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_SETGID_E */ {"setgid", EC_USER, EF_MODIFIES_STATE, 1, {{"gid", PT_GID, PF_DEC} } }, /* PPME_SYSCALL_SETGID_X */ {"setgid", EC_USER, EF_MODIFIES_STATE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_GETUID_E */ {"getuid", EC_USER, EF_DROP_FALCO, 0}, /* PPME_SYSCALL_GETUID_X */ {"getuid", EC_USER, EF_DROP_FALCO, 1, {{"uid", PT_UID, PF_DEC} } }, /* PPME_SYSCALL_GETEUID_E */ {"geteuid", EC_USER, EF_DROP_FALCO, 0 }, /* PPME_SYSCALL_GETEUID_X */ {"geteuid", EC_USER, EF_DROP_FALCO, 1, {{"euid", PT_UID, PF_DEC} } }, /* PPME_SYSCALL_GETGID_E */ {"getgid", EC_USER, EF_DROP_FALCO, 0}, /* PPME_SYSCALL_GETGID_X */ {"getgid", EC_USER, EF_DROP_FALCO, 1, {{"gid", PT_GID, PF_DEC} } }, /* PPME_SYSCALL_GETEGID_E */ {"getegid", EC_USER, EF_DROP_FALCO, 0 }, /* PPME_SYSCALL_GETEGID_X */ {"getegid", EC_USER, EF_DROP_FALCO, 1, {{"egid", PT_GID, PF_DEC} } }, /* PPME_SYSCALL_GETRESUID_E */ {"getresuid", EC_USER, EF_DROP_FALCO, 0 }, /* PPME_SYSCALL_GETRESUID_X */ {"getresuid", EC_USER, EF_DROP_FALCO, 4, {{"res", PT_ERRNO, PF_DEC}, {"ruid", PT_UID, PF_DEC }, {"euid", PT_UID, PF_DEC }, {"suid", PT_UID, PF_DEC } } }, /* PPME_SYSCALL_GETRESGID_E */ {"getresgid", EC_USER, EF_DROP_FALCO, 0 }, /* PPME_SYSCALL_GETRESGID_X */ {"getresgid", EC_USER, EF_DROP_FALCO, 4, {{"res", PT_ERRNO, PF_DEC}, {"rgid", PT_GID, PF_DEC }, {"egid", PT_GID, PF_DEC }, {"sgid", PT_GID, PF_DEC } } }, /* PPME_SYSCALL_EXECVE_15_E */{"execve", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_SYSCALL_EXECVE_15_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 15, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"env", PT_BYTEBUF, PF_NA} } }, /* PPME_CLONE_17_E */{"clone", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_CLONE_17_X */{"clone", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 17, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_FORK_17_E */{"fork", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_SYSCALL_FORK_17_X */{"fork", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 17, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_VFORK_17_E */{"vfork", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_SYSCALL_VFORK_17_X */{"vfork", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 17, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC} } }, /* PPME_CLONE_20_E */{"clone", EC_PROCESS, EF_MODIFIES_STATE, 0}, /* PPME_CLONE_20_X */{"clone", EC_PROCESS, EF_MODIFIES_STATE, 20, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC}, {"vtid", PT_PID, PF_DEC}, {"vpid", PT_PID, PF_DEC} } }, /* PPME_SYSCALL_FORK_20_E */{"fork", EC_PROCESS, EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_FORK_20_X */{"fork", EC_PROCESS, EF_MODIFIES_STATE, 20, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC}, {"vtid", PT_PID, PF_DEC}, {"vpid", PT_PID, PF_DEC} } }, /* PPME_SYSCALL_VFORK_20_E */{"vfork", EC_PROCESS, EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_VFORK_20_X */{"vfork", EC_PROCESS, EF_MODIFIES_STATE, 20, {{"res", PT_PID, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_INT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, clone_flags}, {"uid", PT_UINT32, PF_DEC}, {"gid", PT_UINT32, PF_DEC}, {"vtid", PT_PID, PF_DEC}, {"vpid", PT_PID, PF_DEC} } }, /* PPME_CONTAINER_E */{"container", EC_INTERNAL, EF_SKIPPARSERESET | EF_MODIFIES_STATE, 4, {{"id", PT_CHARBUF, PF_NA}, {"type", PT_UINT32, PF_DEC}, {"name", PT_CHARBUF, PF_NA}, {"image", PT_CHARBUF, PF_NA} } }, /* PPME_CONTAINER_X */{"container", EC_INTERNAL, EF_UNUSED, 0}, /* PPME_SYSCALL_EXECVE_16_E */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_EXECVE_16_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 16, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"env", PT_BYTEBUF, PF_NA} } }, /* PPME_SIGNALDELIVER_E */ {"signaldeliver", EC_SIGNAL, EF_DROP_FALCO, 3, {{"spid", PT_PID, PF_DEC}, {"dpid", PT_PID, PF_DEC}, {"sig", PT_SIGTYPE, PF_DEC} } }, /* PPME_SIGNALDELIVER_X */ {"signaldeliver", EC_SIGNAL, EF_UNUSED, 0 }, /* PPME_PROCINFO_E */{"procinfo", EC_INTERNAL, EF_SKIPPARSERESET | EF_DROP_FALCO, 2, {{"cpu_usr", PT_UINT64, PF_DEC}, {"cpu_sys", PT_UINT64, PF_DEC} } }, /* PPME_PROCINFO_X */{"NA2", EC_INTERNAL, EF_UNUSED, 0}, /* PPME_SYSCALL_GETDENTS_E */{"getdents", EC_FILE, EF_USES_FD | EF_DROP_FALCO, 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_GETDENTS_X */{"getdents", EC_FILE, EF_USES_FD | EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_GETDENTS64_E */{"getdents64", EC_FILE, EF_USES_FD | EF_DROP_FALCO, 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_GETDENTS64_X */{"getdents64", EC_FILE, EF_USES_FD | EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_SETNS_E */ {"setns", EC_PROCESS, EF_USES_FD, 2, {{"fd", PT_FD, PF_NA}, {"nstype", PT_FLAGS32, PF_HEX, clone_flags} } }, /* PPME_SYSCALL_SETNS_X */ {"setns", EC_PROCESS, EF_USES_FD, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_FLOCK_E */ {"flock", EC_FILE, EF_USES_FD, 2, {{"fd", PT_FD, PF_NA}, {"operation", PT_FLAGS32, PF_HEX, flock_flags} } }, /* PPME_SYSCALL_FLOCK_X */ {"flock", EC_FILE, EF_USES_FD, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_CPU_HOTPLUG_E */ {"cpu_hotplug", EC_SYSTEM, EF_SKIPPARSERESET | EF_MODIFIES_STATE, 2, {{"cpu", PT_UINT32, PF_DEC}, {"action", PT_UINT32, PF_DEC} } }, /* PPME_CPU_HOTPLUG_X */{"NA2", EC_SYSTEM, EF_UNUSED, 0}, /* PPME_SOCKET_ACCEPT_5_E */{"accept", EC_NET, EF_CREATES_FD | EF_MODIFIES_STATE, 0}, /* PPME_SOCKET_ACCEPT_5_X */{"accept", EC_NET, EF_CREATES_FD | EF_MODIFIES_STATE, 5, {{"fd", PT_FD, PF_DEC}, {"tuple", PT_SOCKTUPLE, PF_NA}, {"queuepct", PT_UINT8, PF_DEC}, {"queuelen", PT_UINT32, PF_DEC}, {"queuemax", PT_UINT32, PF_DEC} } }, /* PPME_SOCKET_ACCEPT4_5_E */{"accept", EC_NET, EF_CREATES_FD | EF_MODIFIES_STATE, 1, {{"flags", PT_INT32, PF_HEX} } }, /* PPME_SOCKET_ACCEPT4_5_X */{"accept", EC_NET, EF_CREATES_FD | EF_MODIFIES_STATE, 5, {{"fd", PT_FD, PF_DEC}, {"tuple", PT_SOCKTUPLE, PF_NA}, {"queuepct", PT_UINT8, PF_DEC}, {"queuelen", PT_UINT32, PF_DEC}, {"queuemax", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_SEMOP_E */ {"semop", EC_PROCESS, EF_DROP_FALCO, 1, {{"semid", PT_INT32, PF_DEC} } }, /* PPME_SYSCALL_SEMOP_X */ {"semop", EC_PROCESS, EF_DROP_FALCO, 8, {{"res", PT_ERRNO, PF_DEC}, {"nsops", PT_UINT32, PF_DEC}, {"sem_num_0", PT_UINT16, PF_DEC}, {"sem_op_0", PT_INT16, PF_DEC}, {"sem_flg_0", PT_FLAGS16, PF_HEX, semop_flags}, {"sem_num_1", PT_UINT16, PF_DEC}, {"sem_op_1", PT_INT16, PF_DEC}, {"sem_flg_1", PT_FLAGS16, PF_HEX, semop_flags} } }, /* PPME_SYSCALL_SEMCTL_E */{"semctl", EC_PROCESS, EF_DROP_FALCO, 4, {{"semid", PT_INT32, PF_DEC}, {"semnum", PT_INT32, PF_DEC}, {"cmd", PT_FLAGS16, PF_HEX, semctl_commands}, {"val", PT_INT32, PF_DEC} } }, /* PPME_SYSCALL_SEMCTL_X */{"semctl", EC_PROCESS, EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_PPOLL_E */{"ppoll", EC_WAIT, EF_WAITS | EF_DROP_FALCO, 3, {{"fds", PT_FDLIST, PF_DEC}, {"timeout", PT_RELTIME, PF_DEC}, {"sigmask", PT_SIGSET, PF_DEC} } }, /* PPME_SYSCALL_PPOLL_X */{"ppoll", EC_WAIT, EF_WAITS | EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"fds", PT_FDLIST, PF_DEC} } }, /* PPME_SYSCALL_MOUNT_E */{"mount", EC_FILE, EF_MODIFIES_STATE, 1, {{"flags", PT_FLAGS32, PF_HEX, mount_flags} } }, /* PPME_SYSCALL_MOUNT_X */{"mount", EC_FILE, EF_MODIFIES_STATE, 4, {{"res", PT_ERRNO, PF_DEC}, {"dev", PT_CHARBUF, PF_NA}, {"dir", PT_FSPATH, PF_NA}, {"type", PT_CHARBUF, PF_NA} } }, /* PPME_SYSCALL_UMOUNT_E */{"umount", EC_FILE, EF_MODIFIES_STATE, 1, {{"flags", PT_FLAGS32, PF_HEX, umount_flags} } }, /* PPME_SYSCALL_UMOUNT_X */{"umount", EC_FILE, EF_MODIFIES_STATE, 2, {{"res", PT_ERRNO, PF_DEC}, {"name", PT_FSPATH, PF_NA} } }, /* PPME_K8S_E */{"k8s", EC_INTERNAL, EF_SKIPPARSERESET | EF_MODIFIES_STATE, 1, {{"json", PT_CHARBUF, PF_NA} } }, /* PPME_K8S_X */{"NA3", EC_SYSTEM, EF_UNUSED, 0}, /* PPME_SYSCALL_SEMGET_E */{"semget", EC_PROCESS, EF_DROP_FALCO, 3, {{"key", PT_INT32, PF_HEX}, {"nsems", PT_INT32, PF_DEC}, {"semflg", PT_FLAGS32, PF_HEX, semget_flags} } }, /* PPME_SYSCALL_SEMGET_X */{"semget", EC_PROCESS, EF_DROP_FALCO, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_ACCESS_E */{"access", EC_FILE, EF_DROP_FALCO, 1, {{"mode", PT_FLAGS32, PF_HEX, access_flags} } }, /* PPME_SYSCALL_ACCESS_X */{"access", EC_FILE, EF_DROP_FALCO, 2, {{"res", PT_ERRNO, PF_DEC}, {"name", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_CHROOT_E */{"chroot", EC_PROCESS, EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_CHROOT_X */{"chroot", EC_PROCESS, EF_MODIFIES_STATE, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_TRACER_E */{"tracer", EC_OTHER, EF_NONE, 3, {{"id", PT_INT64, PF_DEC}, {"tags", PT_CHARBUFARRAY, PF_NA}, {"args", PT_CHARBUF_PAIR_ARRAY, PF_NA} } }, /* PPME_TRACER_X */{ "tracer", EC_OTHER, EF_NONE, 3, { { "id", PT_INT64, PF_DEC }, { "tags", PT_CHARBUFARRAY, PF_NA }, { "args", PT_CHARBUF_PAIR_ARRAY, PF_NA } } }, /* PPME_MESOS_E */{"mesos", EC_INTERNAL, EF_SKIPPARSERESET | EF_MODIFIES_STATE, 1, {{"json", PT_CHARBUF, PF_NA} } }, /* PPME_MESOS_X */{"NA4", EC_SYSTEM, EF_UNUSED, 0}, /* PPME_CONTAINER_JSON_E */{"container", EC_PROCESS, EF_MODIFIES_STATE, 1, {{"json", PT_CHARBUF, PF_NA} } }, /* PPME_CONTAINER_JSON_X */{"container", EC_PROCESS, EF_UNUSED, 0}, /* PPME_SYSCALL_SETSID_E */{"setsid", EC_PROCESS, EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_SETSID_X */{"setsid", EC_PROCESS, EF_MODIFIES_STATE, 1, {{"res", PT_PID, PF_DEC} } }, /* PPME_SYSCALL_MKDIR_2_E */{"mkdir", EC_FILE, EF_NONE, 1, {{"mode", PT_UINT32, PF_HEX} } }, /* PPME_SYSCALL_MKDIR_2_X */{"mkdir", EC_FILE, EF_NONE, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_RMDIR_2_E */{"rmdir", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_RMDIR_2_X */{"rmdir", EC_FILE, EF_NONE, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_NOTIFICATION_E */{"notification", EC_OTHER, EF_SKIPPARSERESET, 2, {{"id", PT_CHARBUF, PF_DEC}, {"desc", PT_CHARBUF, PF_NA}, } }, /* PPME_NOTIFICATION_X */{"NA4", EC_SYSTEM, EF_UNUSED, 0}, /* PPME_SYSCALL_EXECVE_17_E */{"execve", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 0}, /* PPME_SYSCALL_EXECVE_17_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 17, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"env", PT_BYTEBUF, PF_NA}, {"tty", PT_INT32, PF_DEC} } }, /* PPME_SYSCALL_UNSHARE_E */ {"unshare", EC_PROCESS, EF_NONE, 1, {{"flags", PT_FLAGS32, PF_HEX, clone_flags} } }, /* PPME_SYSCALL_UNSHARE_X */ {"unshare", EC_PROCESS, EF_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_INFRASTRUCTURE_EVENT_E */{"infra", EC_INTERNAL, EF_SKIPPARSERESET, 4, {{"source", PT_CHARBUF, PF_DEC}, {"name", PT_CHARBUF, PF_NA}, {"description", PT_CHARBUF, PF_NA}, {"scope", PT_CHARBUF, PF_NA} } }, /* PPME_INFRASTRUCTURE_EVENT_X */{"NA4", EC_SYSTEM, EF_UNUSED, 0}, /* PPME_SYSCALL_EXECVE_18_E */{"execve", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 1, {{"filename", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_EXECVE_18_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE | EF_OLD_VERSION, 17, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"env", PT_BYTEBUF, PF_NA}, {"tty", PT_INT32, PF_DEC} } }, /* PPME_PAGE_FAULT_E */ {"page_fault", EC_OTHER, EF_SKIPPARSERESET | EF_DROP_FALCO, 3, {{"addr", PT_UINT64, PF_HEX}, {"ip", PT_UINT64, PF_HEX}, {"error", PT_FLAGS32, PF_HEX, pf_flags} } }, /* PPME_PAGE_FAULT_X */ {"NA5", EC_OTHER, EF_UNUSED, 0}, /* PPME_SYSCALL_EXECVE_19_E */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 1, {{"filename", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_EXECVE_19_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 19, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC}, {"pgft_maj", PT_UINT64, PF_DEC}, {"pgft_min", PT_UINT64, PF_DEC}, {"vm_size", PT_UINT32, PF_DEC}, {"vm_rss", PT_UINT32, PF_DEC}, {"vm_swap", PT_UINT32, PF_DEC}, {"comm", PT_CHARBUF, PF_NA}, {"cgroups", PT_BYTEBUF, PF_NA}, {"env", PT_BYTEBUF, PF_NA}, {"tty", PT_INT32, PF_DEC}, {"pgid", PT_PID, PF_DEC}, {"loginuid", PT_INT32, PF_DEC} } }, /* PPME_SYSCALL_SETPGID_E */{"setpgid", EC_PROCESS, EF_MODIFIES_STATE, 2, {{"pid", PT_PID, PF_DEC}, {"pgid", PT_PID, PF_DEC} } }, /* PPME_SYSCALL_SETPGID_X */{"setpgid", EC_PROCESS, EF_MODIFIES_STATE, 1, {{"res", PT_PID, PF_DEC} } }, /* PPME_SYSCALL_BPF_E */{"bpf", EC_OTHER, EF_CREATES_FD, 1, {{"cmd", PT_INT64, PF_DEC} } }, /* PPME_SYSCALL_BPF_X */{"bpf", EC_OTHER, EF_CREATES_FD, 1, {{"res_or_fd", PT_DYN, PF_DEC, bpf_dynamic_param, PPM_BPF_IDX_MAX} } }, /* PPME_SYSCALL_SECCOMP_E */{"seccomp", EC_OTHER, EF_NONE, 1, {{"op", PT_UINT64, PF_DEC}, {"flags", PT_UINT64, PF_HEX} } }, /* PPME_SYSCALL_SECCOMP_X */{"seccomp", EC_OTHER, EF_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_UNLINK_2_E */{"unlink", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_UNLINK_2_X */{"unlink", EC_FILE, EF_NONE, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_UNLINKAT_2_E */{"unlinkat", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_UNLINKAT_2_X */{"unlinkat", EC_FILE, EF_NONE, 4, {{"res", PT_ERRNO, PF_DEC}, {"dirfd", PT_FD, PF_DEC}, {"name", PT_FSPATH, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, unlinkat_flags} } }, /* PPME_SYSCALL_MKDIRAT_E */{"mkdirat", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_MKDIRAT_X */{"mkdirat", EC_FILE, EF_NONE, 4, {{"res", PT_ERRNO, PF_DEC}, {"dirfd", PT_FD, PF_DEC}, {"path", PT_FSPATH, PF_NA}, {"mode", PT_UINT32, PF_HEX} } }, /* PPME_SYSCALL_OPENAT_2_E */{"openat", EC_FILE, EF_CREATES_FD | EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_OPENAT_2_X */{"openat", EC_FILE, EF_CREATES_FD | EF_MODIFIES_STATE, 6, {{"fd", PT_FD, PF_DEC}, {"dirfd", PT_FD, PF_DEC}, {"name", PT_CHARBUF, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, file_flags}, {"mode", PT_UINT32, PF_OCT}, {"dev", PT_UINT32, PF_HEX} } }, /* PPME_SYSCALL_LINK_2_E */{"link", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_LINK_2_X */{"link", EC_FILE, EF_NONE, 3, {{"res", PT_ERRNO, PF_DEC}, {"oldpath", PT_FSPATH, PF_NA}, {"newpath", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_LINKAT_2_E */{"linkat", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_LINKAT_2_X */{"linkat", EC_FILE, EF_NONE, 6, {{"res", PT_ERRNO, PF_DEC}, {"olddir", PT_FD, PF_DEC}, {"oldpath", PT_CHARBUF, PF_NA}, {"newdir", PT_FD, PF_DEC}, {"newpath", PT_CHARBUF, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, linkat_flags} } }, /* PPME_SYSCALL_FCHMODAT_E */{"fchmodat", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_FCHMODAT_X */{"fchmodat", EC_FILE, EF_NONE, 4, {{"res", PT_ERRNO, PF_DEC}, {"dirfd", PT_FD, PF_DEC}, {"filename", PT_FSPATH, PF_NA}, {"mode", PT_MODE, PF_OCT, chmod_mode} } }, /* PPME_SYSCALL_CHMOD_E */{"chmod", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_CHMOD_X */{"chmod", EC_FILE, EF_NONE, 3, {{"res", PT_ERRNO, PF_DEC}, {"filename", PT_FSPATH, PF_NA}, {"mode", PT_MODE, PF_OCT, chmod_mode} } }, /* PPME_SYSCALL_FCHMOD_E */{"fchmod", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_FCHMOD_X */{"fchmod", EC_FILE, EF_NONE, 3, {{"res", PT_ERRNO, PF_DEC}, {"fd", PT_FD, PF_DEC}, {"mode", PT_MODE, PF_OCT, chmod_mode} } } /* NB: Starting from scap version 1.2, event types will no longer be changed when an event is modified, and the only kind of change permitted for pre-existent events is adding parameters. * New event types are allowed only for new syscalls or new internal events. * The number of parameters can be used to differentiate between event versions. */ }; sysdig-0.26.4/driver/fillers_table.c000066400000000000000000000410041352731327100173560ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #include "ppm_events_public.h" #ifdef __KERNEL__ #include "ppm.h" #else #define CAPTURE_CONTEXT_SWITCHES #define CAPTURE_SIGNAL_DELIVERIES #define CAPTURE_PAGE_FAULTS #endif #ifdef __KERNEL__ #define FILLER_REF(x) f_##x, PPM_FILLER_##x #else #define FILLER_REF(x) 0, PPM_FILLER_##x #endif /* __KERNEL__ */ #define f_sys_socket_x f_sys_single_x const struct ppm_event_entry g_ppm_events[PPM_EVENT_MAX] = { [PPME_GENERIC_E] = {FILLER_REF(sys_generic)}, [PPME_GENERIC_X] = {FILLER_REF(sys_generic)}, [PPME_SYSCALL_OPEN_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_OPEN_X] = {FILLER_REF(sys_open_x)}, [PPME_SYSCALL_CLOSE_E] = {FILLER_REF(sys_single)}, [PPME_SYSCALL_CLOSE_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_READ_E] = {FILLER_REF(sys_autofill), 2, APT_REG, {{0}, {2} } }, [PPME_SYSCALL_READ_X] = {FILLER_REF(sys_read_x)}, [PPME_SYSCALL_WRITE_E] = {FILLER_REF(sys_autofill), 2, APT_REG, {{0}, {2} } }, [PPME_SYSCALL_WRITE_X] = {FILLER_REF(sys_write_x)}, [PPME_PROCEXIT_1_E] = {FILLER_REF(sys_procexit_e)}, [PPME_SOCKET_SOCKET_E] = {FILLER_REF(sys_autofill), 3, APT_SOCK, {{0}, {1}, {2} } }, [PPME_SOCKET_SOCKET_X] = {FILLER_REF(sys_socket_x)}, [PPME_SOCKET_BIND_E] = {FILLER_REF(sys_autofill), 1, APT_SOCK, {{0} } }, [PPME_SOCKET_BIND_X] = {FILLER_REF(sys_socket_bind_x)}, [PPME_SOCKET_CONNECT_E] = {FILLER_REF(sys_autofill), 1, APT_SOCK, {{0} } }, [PPME_SOCKET_CONNECT_X] = {FILLER_REF(sys_connect_x)}, [PPME_SOCKET_LISTEN_E] = {FILLER_REF(sys_autofill), 2, APT_SOCK, {{0}, {1} } }, [PPME_SOCKET_LISTEN_X] = {FILLER_REF(sys_single_x)}, [PPME_SOCKET_SEND_E] = {FILLER_REF(sys_send_e)}, [PPME_SOCKET_SEND_X] = {FILLER_REF(sys_send_x)}, [PPME_SOCKET_SENDTO_E] = {FILLER_REF(sys_sendto_e)}, [PPME_SOCKET_SENDTO_X] = {FILLER_REF(sys_send_x)}, [PPME_SOCKET_RECV_E] = {FILLER_REF(sys_autofill), 2, APT_SOCK, {{0}, {2} } }, [PPME_SOCKET_RECV_X] = {FILLER_REF(sys_recv_x)}, [PPME_SOCKET_RECVFROM_E] = {FILLER_REF(sys_autofill), 2, APT_SOCK, {{0}, {2} } }, [PPME_SOCKET_RECVFROM_X] = {FILLER_REF(sys_recvfrom_x)}, [PPME_SOCKET_SHUTDOWN_E] = {FILLER_REF(sys_shutdown_e)}, [PPME_SOCKET_SHUTDOWN_X] = {FILLER_REF(sys_single_x)}, [PPME_SOCKET_GETSOCKNAME_E] = {FILLER_REF(sys_empty)}, [PPME_SOCKET_GETSOCKNAME_X] = {FILLER_REF(sys_empty)}, [PPME_SOCKET_GETPEERNAME_E] = {FILLER_REF(sys_empty)}, [PPME_SOCKET_GETPEERNAME_X] = {FILLER_REF(sys_empty)}, [PPME_SOCKET_SOCKETPAIR_E] = {FILLER_REF(sys_autofill), 3, APT_SOCK, {{0}, {1}, {2} } }, [PPME_SOCKET_SOCKETPAIR_X] = {FILLER_REF(sys_socketpair_x)}, [PPME_SOCKET_SETSOCKOPT_E] = {FILLER_REF(sys_empty)}, [PPME_SOCKET_SETSOCKOPT_X] = {FILLER_REF(sys_setsockopt_x)}, [PPME_SOCKET_GETSOCKOPT_E] = {FILLER_REF(sys_empty)}, [PPME_SOCKET_GETSOCKOPT_X] = {FILLER_REF(sys_getsockopt_x)}, [PPME_SOCKET_SENDMSG_E] = {FILLER_REF(sys_sendmsg_e)}, [PPME_SOCKET_SENDMSG_X] = {FILLER_REF(sys_sendmsg_x)}, [PPME_SOCKET_SENDMMSG_E] = {FILLER_REF(sys_empty)}, [PPME_SOCKET_SENDMMSG_X] = {FILLER_REF(sys_empty)}, [PPME_SOCKET_RECVMSG_E] = {FILLER_REF(sys_autofill), 1, APT_SOCK, {{0} } }, [PPME_SOCKET_RECVMSG_X] = {FILLER_REF(sys_recvmsg_x)}, [PPME_SOCKET_RECVMMSG_E] = {FILLER_REF(sys_empty)}, [PPME_SOCKET_RECVMMSG_X] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_CREAT_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_CREAT_X] = {FILLER_REF(sys_creat_x)}, [PPME_SYSCALL_PIPE_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_PIPE_X] = {FILLER_REF(sys_pipe_x)}, [PPME_SYSCALL_EVENTFD_E] = {FILLER_REF(sys_eventfd_e)}, [PPME_SYSCALL_EVENTFD_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_FUTEX_E] = {FILLER_REF(sys_futex_e)}, [PPME_SYSCALL_FUTEX_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_STAT_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_STAT_X] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_LSTAT_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_LSTAT_X] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_FSTAT_E] = {FILLER_REF(sys_single)}, [PPME_SYSCALL_FSTAT_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_STAT64_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_STAT64_X] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_LSTAT64_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_LSTAT64_X] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_FSTAT64_E] = {FILLER_REF(sys_single)}, [PPME_SYSCALL_FSTAT64_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_EPOLLWAIT_E] = {FILLER_REF(sys_autofill), 1, APT_REG, {{2} } }, [PPME_SYSCALL_EPOLLWAIT_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_POLL_E] = {FILLER_REF(sys_poll_e)}, [PPME_SYSCALL_POLL_X] = {FILLER_REF(sys_poll_x)}, [PPME_SYSCALL_SELECT_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_SELECT_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_NEWSELECT_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_NEWSELECT_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_LSEEK_E] = {FILLER_REF(sys_lseek_e)}, [PPME_SYSCALL_LSEEK_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_LLSEEK_E] = {FILLER_REF(sys_llseek_e)}, [PPME_SYSCALL_LLSEEK_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_GETCWD_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_GETCWD_X] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_CHDIR_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_CHDIR_X] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_FCHDIR_E] = {FILLER_REF(sys_single)}, [PPME_SYSCALL_FCHDIR_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_UNLINK_E] = {FILLER_REF(sys_single)}, [PPME_SYSCALL_UNLINK_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_UNLINKAT_E] = {FILLER_REF(sys_autofill), 2, APT_REG, {{0}, {1} } }, [PPME_SYSCALL_UNLINKAT_X] = {FILLER_REF(sys_single_x)}, #ifdef _64BIT_ARGS_SINGLE_REGISTER [PPME_SYSCALL_PREAD_E] = {FILLER_REF(sys_autofill), 3, APT_REG, {{0}, {2}, {3} } }, #else [PPME_SYSCALL_PREAD_E] = {FILLER_REF(sys_pread64_e)}, #endif [PPME_SYSCALL_PREAD_X] = {FILLER_REF(sys_read_x)}, #ifdef _64BIT_ARGS_SINGLE_REGISTER [PPME_SYSCALL_PWRITE_E] = {FILLER_REF(sys_autofill), 3, APT_REG, {{0}, {2}, {3} } }, #else [PPME_SYSCALL_PWRITE_E] = {FILLER_REF(sys_pwrite64_e)}, #endif [PPME_SYSCALL_PWRITE_X] = {FILLER_REF(sys_write_x)}, [PPME_SYSCALL_READV_E] = {FILLER_REF(sys_single)}, [PPME_SYSCALL_READV_X] = {FILLER_REF(sys_readv_preadv_x)}, [PPME_SYSCALL_WRITEV_E] = {FILLER_REF(sys_writev_e)}, [PPME_SYSCALL_WRITEV_X] = {FILLER_REF(sys_writev_pwritev_x)}, #ifdef _64BIT_ARGS_SINGLE_REGISTER [PPME_SYSCALL_PREADV_E] = {FILLER_REF(sys_autofill), 2, APT_REG, {{0}, {3} } }, #else [PPME_SYSCALL_PREADV_E] = {FILLER_REF(sys_preadv64_e)}, #endif [PPME_SYSCALL_PREADV_X] = {FILLER_REF(sys_readv_preadv_x)}, [PPME_SYSCALL_PWRITEV_E] = {FILLER_REF(sys_pwritev_e)}, [PPME_SYSCALL_PWRITEV_X] = {FILLER_REF(sys_writev_pwritev_x)}, [PPME_SYSCALL_DUP_E] = {FILLER_REF(sys_autofill), 1, APT_REG, {{0} } }, [PPME_SYSCALL_DUP_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_SIGNALFD_E] = {FILLER_REF(sys_autofill), 3, APT_REG, {{0}, {AF_ID_USEDEFAULT, 0}, {AF_ID_USEDEFAULT, 0} } }, [PPME_SYSCALL_SIGNALFD_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_KILL_E] = {FILLER_REF(sys_autofill), 2, APT_REG, {{0}, {1} } }, [PPME_SYSCALL_KILL_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_TKILL_E] = {FILLER_REF(sys_autofill), 2, APT_REG, {{0}, {1} } }, [PPME_SYSCALL_TKILL_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_TGKILL_E] = {FILLER_REF(sys_autofill), 3, APT_REG, {{0}, {1}, {2} } }, [PPME_SYSCALL_TGKILL_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_NANOSLEEP_E] = {FILLER_REF(sys_nanosleep_e)}, [PPME_SYSCALL_NANOSLEEP_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_TIMERFD_CREATE_E] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_USEDEFAULT, 0}, {AF_ID_USEDEFAULT, 0} } }, [PPME_SYSCALL_TIMERFD_CREATE_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_INOTIFY_INIT_E] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_USEDEFAULT, 0} } }, [PPME_SYSCALL_INOTIFY_INIT_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_GETRLIMIT_E] = {FILLER_REF(sys_getrlimit_setrlimit_e)}, [PPME_SYSCALL_GETRLIMIT_X] = {FILLER_REF(sys_getrlimit_setrlrimit_x)}, [PPME_SYSCALL_SETRLIMIT_E] = {FILLER_REF(sys_getrlimit_setrlimit_e)}, [PPME_SYSCALL_SETRLIMIT_X] = {FILLER_REF(sys_getrlimit_setrlrimit_x)}, [PPME_SYSCALL_PRLIMIT_E] = {FILLER_REF(sys_prlimit_e)}, [PPME_SYSCALL_PRLIMIT_X] = {FILLER_REF(sys_prlimit_x)}, [PPME_DROP_E] = {FILLER_REF(sched_drop)}, [PPME_DROP_X] = {FILLER_REF(sched_drop)}, [PPME_SYSCALL_FCNTL_E] = {FILLER_REF(sys_fcntl_e)}, [PPME_SYSCALL_FCNTL_X] = {FILLER_REF(sys_single_x)}, #ifdef CAPTURE_CONTEXT_SWITCHES [PPME_SCHEDSWITCH_6_E] = {FILLER_REF(sched_switch_e)}, #endif [PPME_SYSCALL_BRK_4_E] = {FILLER_REF(sys_autofill), 1, APT_REG, {{0} } }, [PPME_SYSCALL_BRK_4_X] = {FILLER_REF(sys_brk_munmap_mmap_x)}, [PPME_SYSCALL_MMAP_E] = {FILLER_REF(sys_mmap_e)}, [PPME_SYSCALL_MMAP_X] = {FILLER_REF(sys_brk_munmap_mmap_x)}, [PPME_SYSCALL_MMAP2_E] = {FILLER_REF(sys_mmap_e)}, [PPME_SYSCALL_MMAP2_X] = {FILLER_REF(sys_brk_munmap_mmap_x)}, [PPME_SYSCALL_MUNMAP_E] = {FILLER_REF(sys_autofill), 2, APT_REG, {{0}, {1} } }, [PPME_SYSCALL_MUNMAP_X] = {FILLER_REF(sys_brk_munmap_mmap_x)}, [PPME_SYSCALL_SPLICE_E] = {FILLER_REF(sys_autofill), 4, APT_REG, {{0}, {2}, {4}, {5} } }, [PPME_SYSCALL_SPLICE_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_PTRACE_E] = {FILLER_REF(sys_ptrace_e)}, [PPME_SYSCALL_PTRACE_X] = {FILLER_REF(sys_ptrace_x)}, [PPME_SYSCALL_IOCTL_3_E] = {FILLER_REF(sys_autofill), 3, APT_REG, {{0}, {1}, {2} } }, [PPME_SYSCALL_IOCTL_3_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_RENAME_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_RENAME_X] = {FILLER_REF(sys_autofill), 3, APT_REG, {{AF_ID_RETVAL}, {0}, {1} } }, [PPME_SYSCALL_RENAMEAT_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_RENAMEAT_X] = {FILLER_REF(sys_renameat_x)}, [PPME_SYSCALL_SYMLINK_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_SYMLINK_X] = {FILLER_REF(sys_autofill), 3, APT_REG, {{AF_ID_RETVAL}, {0}, {1} } }, [PPME_SYSCALL_SYMLINKAT_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_SYMLINKAT_X] = {FILLER_REF(sys_symlinkat_x)}, [PPME_SYSCALL_SENDFILE_E] = {FILLER_REF(sys_sendfile_e)}, [PPME_SYSCALL_SENDFILE_X] = {FILLER_REF(sys_sendfile_x)}, [PPME_SYSCALL_QUOTACTL_E] = {FILLER_REF(sys_quotactl_e)}, [PPME_SYSCALL_QUOTACTL_X] = {FILLER_REF(sys_quotactl_x)}, [PPME_SYSCALL_SETRESUID_E] = {FILLER_REF(sys_autofill), 3, APT_REG, {{0}, {1}, {2} } }, [PPME_SYSCALL_SETRESUID_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_SETRESGID_E] = {FILLER_REF(sys_autofill), 3, APT_REG, {{0}, {1}, {2} } }, [PPME_SYSCALL_SETRESGID_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSDIGEVENT_E] = {FILLER_REF(sys_sysdigevent_e)}, [PPME_SYSCALL_SETUID_E] = {FILLER_REF(sys_autofill), 1, APT_REG, {{0} } }, [PPME_SYSCALL_SETUID_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_SETGID_E] = {FILLER_REF(sys_autofill), 1, APT_REG, {{0} } }, [PPME_SYSCALL_SETGID_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_GETUID_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_GETUID_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_GETEUID_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_GETEUID_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_GETGID_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_GETGID_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_GETEGID_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_GETEGID_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_GETRESUID_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_GETRESUID_X] = {FILLER_REF(sys_getresuid_and_gid_x)}, [PPME_SYSCALL_GETRESGID_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_GETRESGID_X] = {FILLER_REF(sys_getresuid_and_gid_x)}, [PPME_SYSCALL_CLONE_20_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_CLONE_20_X] = {FILLER_REF(proc_startupdate)}, [PPME_SYSCALL_FORK_20_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_FORK_20_X] = {FILLER_REF(proc_startupdate)}, [PPME_SYSCALL_VFORK_20_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_VFORK_20_X] = {FILLER_REF(proc_startupdate)}, #ifdef CAPTURE_SIGNAL_DELIVERIES [PPME_SIGNALDELIVER_E] = {FILLER_REF(sys_signaldeliver_e)}, [PPME_SIGNALDELIVER_X] = {FILLER_REF(sys_empty)}, #endif [PPME_SYSCALL_GETDENTS_E] = {FILLER_REF(sys_single)}, [PPME_SYSCALL_GETDENTS_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_GETDENTS64_E] = {FILLER_REF(sys_single)}, [PPME_SYSCALL_GETDENTS64_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_SETNS_E] = {FILLER_REF(sys_setns_e)}, [PPME_SYSCALL_SETNS_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_FLOCK_E] = {FILLER_REF(sys_flock_e)}, [PPME_SYSCALL_FLOCK_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_CPU_HOTPLUG_E] = {FILLER_REF(cpu_hotplug_e)}, [PPME_SOCKET_ACCEPT_5_E] = {FILLER_REF(sys_empty)}, [PPME_SOCKET_ACCEPT_5_X] = {FILLER_REF(sys_accept_x)}, [PPME_SOCKET_ACCEPT4_5_E] = {FILLER_REF(sys_accept4_e)}, [PPME_SOCKET_ACCEPT4_5_X] = {FILLER_REF(sys_accept_x)}, [PPME_SYSCALL_SEMOP_E] = {FILLER_REF(sys_single)}, [PPME_SYSCALL_SEMOP_X] = {FILLER_REF(sys_semop_x)}, [PPME_SYSCALL_SEMCTL_E] = {FILLER_REF(sys_semctl_e)}, [PPME_SYSCALL_SEMCTL_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_PPOLL_E] = {FILLER_REF(sys_ppoll_e)}, [PPME_SYSCALL_PPOLL_X] = {FILLER_REF(sys_poll_x)}, /* exit same for poll() and ppoll() */ [PPME_SYSCALL_MOUNT_E] = {FILLER_REF(sys_mount_e)}, [PPME_SYSCALL_MOUNT_X] = {FILLER_REF(sys_autofill), 4, APT_REG, {{AF_ID_RETVAL}, {0}, {1}, {2} } }, [PPME_SYSCALL_UMOUNT_E] = {FILLER_REF(sys_autofill), 1, APT_REG, {{1} } }, [PPME_SYSCALL_UMOUNT_X] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_SEMGET_E] = {FILLER_REF(sys_semget_e)}, [PPME_SYSCALL_SEMGET_X] = {FILLER_REF(sys_single_x)}, [PPME_SYSCALL_ACCESS_E] = {FILLER_REF(sys_access_e)}, [PPME_SYSCALL_ACCESS_X] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_CHROOT_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_CHROOT_X] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_SETSID_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_SETSID_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_SETPGID_E] = {FILLER_REF(sys_autofill), 2, APT_REG, {{0}, {1} } }, [PPME_SYSCALL_SETPGID_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_MKDIR_2_E] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_USEDEFAULT, 0} } }, [PPME_SYSCALL_MKDIR_2_X] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_RMDIR_2_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_RMDIR_2_X] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_UNSHARE_E] = {FILLER_REF(sys_unshare_e)}, [PPME_SYSCALL_UNSHARE_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_EXECVE_19_E] = {FILLER_REF(sys_execve_e)}, [PPME_SYSCALL_EXECVE_19_X] = {FILLER_REF(proc_startupdate)}, #ifdef CAPTURE_PAGE_FAULTS [PPME_PAGE_FAULT_E] = {FILLER_REF(sys_pagefault_e)}, [PPME_PAGE_FAULT_X] = {FILLER_REF(sys_empty)}, #endif [PPME_SYSCALL_BPF_E] = {FILLER_REF(sys_autofill), 1, APT_REG, {{0} } }, [PPME_SYSCALL_BPF_X] = {FILLER_REF(sys_bpf_x)}, [PPME_SYSCALL_SECCOMP_E] = {FILLER_REF(sys_autofill), 1, APT_REG, {{0}, {1} } }, [PPME_SYSCALL_SECCOMP_X] = {FILLER_REF(sys_autofill), 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_UNLINK_2_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_UNLINK_2_X] = {FILLER_REF(sys_autofill), 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_UNLINKAT_2_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_UNLINKAT_2_X] = {FILLER_REF(sys_unlinkat_x)}, [PPME_SYSCALL_MKDIRAT_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_MKDIRAT_X] = {FILLER_REF(sys_mkdirat_x)}, [PPME_SYSCALL_OPENAT_2_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_OPENAT_2_X] = {FILLER_REF(sys_openat_x)}, [PPME_SYSCALL_LINK_2_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_LINK_2_X] = {FILLER_REF(sys_autofill), 3, APT_REG, {{AF_ID_RETVAL}, {0}, {1} } }, [PPME_SYSCALL_LINKAT_2_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_LINKAT_2_X] = {FILLER_REF(sys_linkat_x)}, [PPME_SYSCALL_FCHMODAT_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_FCHMODAT_X] = {FILLER_REF(sys_fchmodat_x)}, [PPME_SYSCALL_CHMOD_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_CHMOD_X] = {FILLER_REF(sys_chmod_x)}, [PPME_SYSCALL_FCHMOD_E] = {FILLER_REF(sys_empty)}, [PPME_SYSCALL_FCHMOD_X] = {FILLER_REF(sys_fchmod_x)} }; sysdig-0.26.4/driver/flags_table.c000066400000000000000000000377171352731327100170320ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #include "ppm_events_public.h" const struct ppm_name_value socket_families[] = { {"AF_NFC", PPM_AF_NFC}, {"AF_ALG", PPM_AF_ALG}, {"AF_CAIF", PPM_AF_CAIF}, {"AF_IEEE802154", PPM_AF_IEEE802154}, {"AF_PHONET", PPM_AF_PHONET}, {"AF_ISDN", PPM_AF_ISDN}, {"AF_RXRPC", PPM_AF_RXRPC}, {"AF_IUCV", PPM_AF_IUCV}, {"AF_BLUETOOTH", PPM_AF_BLUETOOTH}, {"AF_TIPC", PPM_AF_TIPC}, {"AF_CAN", PPM_AF_CAN}, {"AF_LLC", PPM_AF_LLC}, {"AF_WANPIPE", PPM_AF_WANPIPE}, {"AF_PPPOX", PPM_AF_PPPOX}, {"AF_IRDA", PPM_AF_IRDA}, {"AF_SNA", PPM_AF_SNA}, {"AF_RDS", PPM_AF_RDS}, {"AF_ATMSVC", PPM_AF_ATMSVC}, {"AF_ECONET", PPM_AF_ECONET}, {"AF_ASH", PPM_AF_ASH}, {"AF_PACKET", PPM_AF_PACKET}, {"AF_ROUTE", PPM_AF_ROUTE}, {"AF_NETLINK", PPM_AF_NETLINK}, {"AF_KEY", PPM_AF_KEY}, {"AF_SECURITY", PPM_AF_SECURITY}, {"AF_NETBEUI", PPM_AF_NETBEUI}, {"AF_DECnet", PPM_AF_DECnet}, {"AF_ROSE", PPM_AF_ROSE}, {"AF_INET6", PPM_AF_INET6}, {"AF_X25", PPM_AF_X25}, {"AF_ATMPVC", PPM_AF_ATMPVC}, {"AF_BRIDGE", PPM_AF_BRIDGE}, {"AF_NETROM", PPM_AF_NETROM}, {"AF_APPLETALK", PPM_AF_APPLETALK}, {"AF_IPX", PPM_AF_IPX}, {"AF_AX25", PPM_AF_AX25}, {"AF_INET", PPM_AF_INET}, {"AF_LOCAL", PPM_AF_LOCAL}, {"AF_UNIX", PPM_AF_UNIX}, {"AF_UNSPEC", PPM_AF_UNSPEC}, {0, 0}, }; const struct ppm_name_value file_flags[] = { {"O_LARGEFILE", PPM_O_LARGEFILE}, {"O_DIRECTORY", PPM_O_DIRECTORY}, {"O_DIRECT", PPM_O_DIRECT}, {"O_TRUNC", PPM_O_TRUNC}, {"O_SYNC", PPM_O_SYNC}, {"O_NONBLOCK", PPM_O_NONBLOCK}, {"O_EXCL", PPM_O_EXCL}, {"O_DSYNC", PPM_O_DSYNC}, {"O_APPEND", PPM_O_APPEND}, {"O_CREAT", PPM_O_CREAT}, {"O_RDWR", PPM_O_RDWR}, {"O_WRONLY", PPM_O_WRONLY}, {"O_RDONLY", PPM_O_RDONLY}, {"O_CLOEXEC", PPM_O_CLOEXEC}, {"O_NONE", PPM_O_NONE}, {0, 0}, }; const struct ppm_name_value flock_flags[] = { {"LOCK_SH", PPM_LOCK_SH}, {"LOCK_EX", PPM_LOCK_EX}, {"LOCK_NB", PPM_LOCK_NB}, {"LOCK_UN", PPM_LOCK_UN}, {"LOCK_NONE", PPM_LOCK_NONE}, {0, 0}, }; const struct ppm_name_value clone_flags[] = { {"CLONE_FILES", PPM_CL_CLONE_FILES}, {"CLONE_FS", PPM_CL_CLONE_FS}, {"CLONE_IO", PPM_CL_CLONE_IO}, {"CLONE_NEWIPC", PPM_CL_CLONE_NEWIPC}, {"CLONE_NEWNET", PPM_CL_CLONE_NEWNET}, {"CLONE_NEWNS", PPM_CL_CLONE_NEWNS}, {"CLONE_NEWPID", PPM_CL_CLONE_NEWPID}, {"CLONE_NEWUTS", PPM_CL_CLONE_NEWUTS}, {"CLONE_PARENT", PPM_CL_CLONE_PARENT}, {"CLONE_PARENT_SETTID", PPM_CL_CLONE_PARENT_SETTID}, {"CLONE_PTRACE", PPM_CL_CLONE_PTRACE}, {"CLONE_SIGHAND", PPM_CL_CLONE_SIGHAND}, {"CLONE_SYSVSEM", PPM_CL_CLONE_SYSVSEM}, {"CLONE_THREAD", PPM_CL_CLONE_THREAD}, {"CLONE_UNTRACED", PPM_CL_CLONE_UNTRACED}, {"CLONE_VM", PPM_CL_CLONE_VM}, {"CLONE_INVERTED", PPM_CL_CLONE_INVERTED}, {"NAME_CHANGED", PPM_CL_NAME_CHANGED}, {"CLOSED", PPM_CL_CLOSED}, {"CLONE_NEWUSER", PPM_CL_CLONE_NEWUSER}, {"CLONE_CHILD_CLEARTID", PPM_CL_CLONE_CHILD_CLEARTID}, {"CLONE_CHILD_SETTID", PPM_CL_CLONE_CHILD_SETTID}, {"CLONE_SETTLS", PPM_CL_CLONE_SETTLS}, {"CLONE_STOPPED", PPM_CL_CLONE_STOPPED}, {"CLONE_VFORK", PPM_CL_CLONE_VFORK}, {"CLONE_NEWCGROUP", PPM_CL_CLONE_NEWCGROUP}, {0, 0}, }; const struct ppm_name_value futex_operations[] = { {"FUTEX_CLOCK_REALTIME", PPM_FU_FUTEX_CLOCK_REALTIME}, {"FUTEX_PRIVATE_FLAG", PPM_FU_FUTEX_PRIVATE_FLAG}, {"FUTEX_CMP_REQUEUE_PI", PPM_FU_FUTEX_CMP_REQUEUE_PI}, {"FUTEX_WAIT_REQUEUE_PI", PPM_FU_FUTEX_WAIT_REQUEUE_PI}, {"FUTEX_WAKE_BITSET", PPM_FU_FUTEX_WAKE_BITSET}, {"FUTEX_WAIT_BITSET", PPM_FU_FUTEX_WAIT_BITSET}, {"FUTEX_TRYLOCK_PI", PPM_FU_FUTEX_TRYLOCK_PI}, {"FUTEX_UNLOCK_PI", PPM_FU_FUTEX_UNLOCK_PI}, {"FUTEX_LOCK_PI", PPM_FU_FUTEX_LOCK_PI}, {"FUTEX_WAKE_OP", PPM_FU_FUTEX_WAKE_OP}, {"FUTEX_CMP_REQUEUE", PPM_FU_FUTEX_CMP_REQUEUE}, {"FUTEX_REQUEUE", PPM_FU_FUTEX_REQUEUE}, {"FUTEX_FD", PPM_FU_FUTEX_FD}, {"FUTEX_WAKE", PPM_FU_FUTEX_WAKE}, {"FUTEX_WAIT", PPM_FU_FUTEX_WAIT}, {0, 0}, }; const struct ppm_name_value poll_flags[] = { {"POLLIN", PPM_POLLIN}, {"POLLPRI", PPM_POLLPRI}, {"POLLOUT", PPM_POLLOUT}, {"POLLRDHUP", PPM_POLLRDHUP}, {"POLLERR", PPM_POLLERR}, {"POLLHUP", PPM_POLLHUP}, {"POLLNVAL", PPM_POLLNVAL}, {"POLLRDNORM", PPM_POLLRDNORM}, {"POLLRDBAND", PPM_POLLRDBAND}, {"POLLWRNORM", PPM_POLLWRNORM}, {"POLLWRBAND", PPM_POLLWRBAND}, {0, 0}, }; /* http://lxr.free-electrons.com/source/include/uapi/linux/fs.h?v=4.2#L65 */ const struct ppm_name_value mount_flags[] = { {"RDONLY", PPM_MS_RDONLY}, {"NOSUID", PPM_MS_NOSUID}, {"NODEV", PPM_MS_NODEV}, {"NOEXEC", PPM_MS_NOEXEC}, {"SYNCHRONOUS", PPM_MS_SYNCHRONOUS}, {"REMOUNT", PPM_MS_REMOUNT}, {"MANDLOCK", PPM_MS_MANDLOCK}, {"DIRSYNC", PPM_MS_DIRSYNC}, {"NOATIME", PPM_MS_NOATIME}, {"NODIRATIME", PPM_MS_NODIRATIME}, {"BIND", PPM_MS_BIND}, {"MOVE", PPM_MS_MOVE}, {"REC", PPM_MS_REC}, {"SILENT", PPM_MS_SILENT}, {"POSIXACL", PPM_MS_POSIXACL}, {"UNBINDABLE", PPM_MS_UNBINDABLE}, {"PRIVATE", PPM_MS_PRIVATE}, {"SLAVE", PPM_MS_SLAVE}, {"SHARED", PPM_MS_SHARED}, {"RELATIME", PPM_MS_RELATIME}, {"KERNMOUNT", PPM_MS_KERNMOUNT}, {"I_VERSION", PPM_MS_I_VERSION}, {"STRICTATIME", PPM_MS_STRICTATIME}, {"LAZYTIME", PPM_MS_LAZYTIME}, {"NOSEC", PPM_MS_NOSEC}, {"BORN", PPM_MS_BORN}, {"ACTIVE", PPM_MS_ACTIVE}, {"NOUSER", PPM_MS_NOUSER}, {0, 0}, }; /* http://lxr.free-electrons.com/source/include/linux/fs.h?v=4.2#L1251 */ const struct ppm_name_value umount_flags[] = { {"FORCE", PPM_MNT_FORCE}, {"DETACH", PPM_MNT_DETACH}, {"EXPIRE", PPM_MNT_EXPIRE}, {"NOFOLLOW", PPM_UMOUNT_NOFOLLOW}, {0, 0}, }; const struct ppm_name_value lseek_whence[] = { {"SEEK_END", PPM_SEEK_END}, {"SEEK_CUR", PPM_SEEK_CUR}, {"SEEK_SET", PPM_SEEK_SET}, {0, 0}, }; const struct ppm_name_value shutdown_how[] = { {"SHUT_RDWR", PPM_SHUT_RDWR}, {"SHUT_WR", PPM_SHUT_WR}, {"SHUT_RD", PPM_SHUT_RD}, {0, 0}, }; const struct ppm_name_value rlimit_resources[] = { {"RLIMIT_UNKNOWN", PPM_RLIMIT_UNKNOWN}, {"RLIMIT_RTTIME", PPM_RLIMIT_RTTIME}, {"RLIMIT_RTPRIO", PPM_RLIMIT_RTPRIO}, {"RLIMIT_NICE", PPM_RLIMIT_NICE}, {"RLIMIT_MSGQUEUE", PPM_RLIMIT_MSGQUEUE}, {"RLIMIT_SIGPENDING", PPM_RLIMIT_SIGPENDING}, {"RLIMIT_LOCKS", PPM_RLIMIT_LOCKS}, {"RLIMIT_AS", PPM_RLIMIT_AS}, {"RLIMIT_MEMLOCK", PPM_RLIMIT_MEMLOCK}, {"RLIMIT_NOFILE", PPM_RLIMIT_NOFILE}, {"RLIMIT_NPROC", PPM_RLIMIT_NPROC}, {"RLIMIT_RSS", PPM_RLIMIT_RSS}, {"RLIMIT_CORE", PPM_RLIMIT_CORE}, {"RLIMIT_STACK", PPM_RLIMIT_STACK}, {"RLIMIT_DATA", PPM_RLIMIT_DATA}, {"RLIMIT_FSIZE", PPM_RLIMIT_FSIZE}, {"RLIMIT_CPU", PPM_RLIMIT_CPU}, {0, 0}, }; const struct ppm_name_value fcntl_commands[] = { {"F_GETPIPE_SZ", PPM_FCNTL_F_GETPIPE_SZ}, {"F_SETPIPE_SZ", PPM_FCNTL_F_SETPIPE_SZ}, {"F_NOTIFY", PPM_FCNTL_F_NOTIFY}, {"F_DUPFD_CLOEXEC", PPM_FCNTL_F_DUPFD_CLOEXEC}, {"F_CANCELLK", PPM_FCNTL_F_CANCELLK}, {"F_GETLEASE", PPM_FCNTL_F_GETLEASE}, {"F_SETLEASE", PPM_FCNTL_F_SETLEASE}, {"F_GETOWN_EX", PPM_FCNTL_F_GETOWN_EX}, {"F_SETOWN_EX", PPM_FCNTL_F_SETOWN_EX}, #ifndef CONFIG_64BIT {"F_SETLKW64", PPM_FCNTL_F_SETLKW64}, {"F_SETLK64", PPM_FCNTL_F_SETLK64}, {"F_GETLK64", PPM_FCNTL_F_GETLK64}, #endif {"F_GETSIG", PPM_FCNTL_F_GETSIG}, {"F_SETSIG", PPM_FCNTL_F_SETSIG}, {"F_GETOWN", PPM_FCNTL_F_GETOWN}, {"F_SETOWN", PPM_FCNTL_F_SETOWN}, {"F_SETLKW", PPM_FCNTL_F_SETLKW}, {"F_SETLK", PPM_FCNTL_F_SETLK}, {"F_GETLK", PPM_FCNTL_F_GETLK}, {"F_SETFL", PPM_FCNTL_F_SETFL}, {"F_GETFL", PPM_FCNTL_F_GETFL}, {"F_SETFD", PPM_FCNTL_F_SETFD}, {"F_GETFD", PPM_FCNTL_F_GETFD}, {"F_DUPFD", PPM_FCNTL_F_DUPFD}, {"F_OFD_GETLK", PPM_FCNTL_F_OFD_GETLK}, {"F_OFD_SETLK", PPM_FCNTL_F_OFD_SETLK}, {"F_OFD_SETLKW", PPM_FCNTL_F_OFD_SETLKW}, {"UNKNOWN", PPM_FCNTL_UNKNOWN}, {0, 0}, }; const struct ppm_name_value sockopt_levels[] = { {"SOL_SOCKET", PPM_SOCKOPT_LEVEL_SOL_SOCKET}, {"SOL_TCP", PPM_SOCKOPT_LEVEL_SOL_TCP}, {"UNKNOWN", PPM_SOCKOPT_LEVEL_UNKNOWN}, {0, 0}, }; const struct ppm_name_value sockopt_options[] = { {"SO_COOKIE", PPM_SOCKOPT_SO_COOKIE}, {"SO_MEMINFO", PPM_SOCKOPT_SO_MEMINFO}, {"SO_PEERGROUPS", PPM_SOCKOPT_SO_PEERGROUPS}, {"SO_ATTACH_BPF", PPM_SOCKOPT_SO_ATTACH_BPF}, {"SO_INCOMING_CPU", PPM_SOCKOPT_SO_INCOMING_CPU}, {"SO_BPF_EXTENSIONS", PPM_SOCKOPT_SO_BPF_EXTENSIONS}, {"SO_MAX_PACING_RATE", PPM_SOCKOPT_SO_MAX_PACING_RATE}, {"SO_BUSY_POLL", PPM_SOCKOPT_SO_BUSY_POLL}, {"SO_SELECT_ERR_QUEUE", PPM_SOCKOPT_SO_SELECT_ERR_QUEUE}, {"SO_LOCK_FILTER", PPM_SOCKOPT_SO_LOCK_FILTER}, {"SO_NOFCS", PPM_SOCKOPT_SO_NOFCS}, {"SO_PEEK_OFF", PPM_SOCKOPT_SO_PEEK_OFF}, {"SO_WIFI_STATUS", PPM_SOCKOPT_SO_WIFI_STATUS}, {"SO_RXQ_OVFL", PPM_SOCKOPT_SO_RXQ_OVFL}, {"SO_DOMAIN", PPM_SOCKOPT_SO_DOMAIN}, {"SO_PROTOCOL", PPM_SOCKOPT_SO_PROTOCOL}, {"SO_TIMESTAMPING", PPM_SOCKOPT_SO_TIMESTAMPING}, {"SO_MARK", PPM_SOCKOPT_SO_MARK}, {"SO_TIMESTAMPNS", PPM_SOCKOPT_SO_TIMESTAMPNS}, {"SO_PASSSEC", PPM_SOCKOPT_SO_PASSSEC}, {"SO_PEERSEC", PPM_SOCKOPT_SO_PEERSEC}, {"SO_ACCEPTCONN", PPM_SOCKOPT_SO_ACCEPTCONN}, {"SO_TIMESTAMP", PPM_SOCKOPT_SO_TIMESTAMP}, {"SO_PEERNAME", PPM_SOCKOPT_SO_PEERNAME}, {"SO_DETACH_FILTER", PPM_SOCKOPT_SO_DETACH_FILTER}, {"SO_ATTACH_FILTER", PPM_SOCKOPT_SO_ATTACH_FILTER}, {"SO_BINDTODEVICE", PPM_SOCKOPT_SO_BINDTODEVICE}, {"SO_SECURITY_ENCRYPTION_NETWORK", PPM_SOCKOPT_SO_SECURITY_ENCRYPTION_NETWORK}, {"SO_SECURITY_ENCRYPTION_TRANSPORT", PPM_SOCKOPT_SO_SECURITY_ENCRYPTION_TRANSPORT}, {"SO_SECURITY_AUTHENTICATION", PPM_SOCKOPT_SO_SECURITY_AUTHENTICATION}, {"SO_SNDTIMEO", PPM_SOCKOPT_SO_SNDTIMEO}, {"SO_RCVTIMEO", PPM_SOCKOPT_SO_RCVTIMEO}, {"SO_SNDLOWAT", PPM_SOCKOPT_SO_SNDLOWAT}, {"SO_RCVLOWAT", PPM_SOCKOPT_SO_RCVLOWAT}, {"SO_PEERCRED", PPM_SOCKOPT_SO_PEERCRED}, {"SO_PASSCRED", PPM_SOCKOPT_SO_PASSCRED}, {"SO_REUSEPORT", PPM_SOCKOPT_SO_REUSEPORT}, {"SO_BSDCOMPAT", PPM_SOCKOPT_SO_BSDCOMPAT}, {"SO_LINGER", PPM_SOCKOPT_SO_LINGER}, {"SO_PRIORITY", PPM_SOCKOPT_SO_PRIORITY}, {"SO_NO_CHECK", PPM_SOCKOPT_SO_NO_CHECK}, {"SO_OOBINLINE", PPM_SOCKOPT_SO_OOBINLINE}, {"SO_KEEPALIVE", PPM_SOCKOPT_SO_KEEPALIVE}, {"SO_RCVBUFFORCE", PPM_SOCKOPT_SO_RCVBUFFORCE}, {"SO_SNDBUFFORCE", PPM_SOCKOPT_SO_SNDBUFFORCE}, {"SO_RCVBUF", PPM_SOCKOPT_SO_RCVBUF}, {"SO_SNDBUF", PPM_SOCKOPT_SO_SNDBUF}, {"SO_BROADCAST", PPM_SOCKOPT_SO_BROADCAST}, {"SO_DONTROUTE", PPM_SOCKOPT_SO_DONTROUTE}, {"SO_ERROR", PPM_SOCKOPT_SO_ERROR}, {"SO_TYPE", PPM_SOCKOPT_SO_TYPE}, {"SO_REUSEADDR", PPM_SOCKOPT_SO_REUSEADDR}, {"SO_DEBUG", PPM_SOCKOPT_SO_DEBUG}, {"UNKNOWN", PPM_SOCKOPT_UNKNOWN}, {0, 0}, }; const struct ppm_name_value ptrace_requests[] = { {"PTRACE_SINGLEBLOCK", PPM_PTRACE_SINGLEBLOCK}, {"PTRACE_SYSEMU_SINGLESTEP", PPM_PTRACE_SYSEMU_SINGLESTEP}, {"PTRACE_SYSEMU", PPM_PTRACE_SYSEMU}, {"PTRACE_ARCH_PRCTL", PPM_PTRACE_ARCH_PRCTL}, {"PTRACE_SET_THREAD_AREA", PPM_PTRACE_SET_THREAD_AREA}, {"PTRACE_GET_THREAD_AREA", PPM_PTRACE_GET_THREAD_AREA}, {"PTRACE_OLDSETOPTIONS", PPM_PTRACE_OLDSETOPTIONS}, {"PTRACE_SETFPXREGS", PPM_PTRACE_SETFPXREGS}, {"PTRACE_GETFPXREGS", PPM_PTRACE_GETFPXREGS}, {"PTRACE_SETFPREGS", PPM_PTRACE_SETFPREGS}, {"PTRACE_GETFPREGS", PPM_PTRACE_GETFPREGS}, {"PTRACE_SETREGS", PPM_PTRACE_SETREGS}, {"PTRACE_GETREGS", PPM_PTRACE_GETREGS}, {"PTRACE_SETSIGMASK", PPM_PTRACE_SETSIGMASK}, {"PTRACE_GETSIGMASK", PPM_PTRACE_GETSIGMASK}, {"PTRACE_PEEKSIGINFO", PPM_PTRACE_PEEKSIGINFO}, {"PTRACE_LISTEN", PPM_PTRACE_LISTEN}, {"PTRACE_INTERRUPT", PPM_PTRACE_INTERRUPT}, {"PTRACE_SEIZE", PPM_PTRACE_SEIZE}, {"PTRACE_SETREGSET", PPM_PTRACE_SETREGSET}, {"PTRACE_GETREGSET", PPM_PTRACE_GETREGSET}, {"PTRACE_SETSIGINFO", PPM_PTRACE_SETSIGINFO}, {"PTRACE_GETSIGINFO", PPM_PTRACE_GETSIGINFO}, {"PTRACE_GETEVENTMSG", PPM_PTRACE_GETEVENTMSG}, {"PTRACE_SETOPTIONS", PPM_PTRACE_SETOPTIONS}, {"PTRACE_SYSCALL", PPM_PTRACE_SYSCALL}, {"PTRACE_DETACH", PPM_PTRACE_DETACH}, {"PTRACE_ATTACH", PPM_PTRACE_ATTACH}, {"PTRACE_SINGLESTEP", PPM_PTRACE_SINGLESTEP}, {"PTRACE_KILL", PPM_PTRACE_KILL}, {"PTRACE_CONT", PPM_PTRACE_CONT}, {"PTRACE_POKEUSR", PPM_PTRACE_POKEUSR}, {"PTRACE_POKEDATA", PPM_PTRACE_POKEDATA}, {"PTRACE_POKETEXT", PPM_PTRACE_POKETEXT}, {"PTRACE_PEEKUSR", PPM_PTRACE_PEEKUSR}, {"PTRACE_PEEKDATA", PPM_PTRACE_PEEKDATA}, {"PTRACE_PEEKTEXT", PPM_PTRACE_PEEKTEXT}, {"PTRACE_TRACEME", PPM_PTRACE_TRACEME}, {"PTRACE_UNKNOWN", PPM_PTRACE_UNKNOWN}, {0, 0}, }; const struct ppm_name_value prot_flags[] = { {"PROT_READ", PPM_PROT_READ}, {"PROT_WRITE", PPM_PROT_WRITE}, {"PROT_EXEC", PPM_PROT_EXEC}, {"PROT_SEM", PPM_PROT_SEM}, {"PROT_GROWSDOWN", PPM_PROT_GROWSDOWN}, {"PROT_GROWSUP", PPM_PROT_GROWSUP}, {"PROT_SAO", PPM_PROT_SAO}, {"PROT_NONE", PPM_PROT_NONE}, {0, 0}, }; const struct ppm_name_value mmap_flags[] = { {"MAP_SHARED", PPM_MAP_SHARED}, {"MAP_PRIVATE", PPM_MAP_PRIVATE}, {"MAP_FIXED", PPM_MAP_FIXED}, {"MAP_ANONYMOUS", PPM_MAP_ANONYMOUS}, {"MAP_32BIT", PPM_MAP_32BIT}, {"MAP_RENAME", PPM_MAP_RENAME}, {"MAP_NORESERVE", PPM_MAP_NORESERVE}, {"MAP_POPULATE", PPM_MAP_POPULATE}, {"MAP_NONBLOCK", PPM_MAP_NONBLOCK}, {"MAP_GROWSDOWN", PPM_MAP_GROWSDOWN}, {"MAP_DENYWRITE", PPM_MAP_DENYWRITE}, {"MAP_EXECUTABLE", PPM_MAP_EXECUTABLE}, {"MAP_INHERIT", PPM_MAP_INHERIT}, {"MAP_FILE", PPM_MAP_FILE}, {"MAP_LOCKED", PPM_MAP_LOCKED}, {0, 0}, }; const struct ppm_name_value splice_flags[] = { {"SPLICE_F_MOVE", PPM_SPLICE_F_MOVE}, {"SPLICE_F_NONBLOCK", PPM_SPLICE_F_NONBLOCK}, {"SPLICE_F_MORE", PPM_SPLICE_F_MORE}, {"SPLICE_F_GIFT", PPM_SPLICE_F_GIFT}, {0, 0}, }; const struct ppm_name_value quotactl_dqi_flags[] = { {"DQF_NONE", PPM_DQF_NONE}, {"V1_DQF_RSQUASH", PPM_V1_DQF_RSQUASH}, {0, 0}, }; const struct ppm_name_value quotactl_cmds[] = { {"Q_QUOTAON", PPM_Q_QUOTAON}, {"Q_QUOTAOFF", PPM_Q_QUOTAOFF}, {"Q_GETFMT", PPM_Q_GETFMT}, {"Q_GETINFO", PPM_Q_GETINFO}, {"Q_SETINFO", PPM_Q_SETINFO}, {"Q_GETQUOTA", PPM_Q_GETQUOTA}, {"Q_SETQUOTA", PPM_Q_SETQUOTA}, {"Q_SYNC", PPM_Q_SYNC}, {"Q_XQUOTAON", PPM_Q_XQUOTAON}, {"Q_XQUOTAOFF", PPM_Q_XQUOTAOFF}, {"Q_XGETQUOTA", PPM_Q_XGETQUOTA}, {"Q_XSETQLIM", PPM_Q_XSETQLIM}, {"Q_XGETQSTAT", PPM_Q_XGETQSTAT}, {"Q_XQUOTARM", PPM_Q_XQUOTARM}, {"Q_XQUOTASYNC", PPM_Q_XQUOTASYNC}, {0, 0}, }; const struct ppm_name_value quotactl_types[] = { {"USRQUOTA", PPM_USRQUOTA}, {"GRPQUOTA", PPM_GRPQUOTA}, {0, 0}, }; const struct ppm_name_value quotactl_quota_fmts[] = { {"QFMT_NOT_USED", PPM_QFMT_NOT_USED}, {"QFMT_VFS_OLD", PPM_QFMT_VFS_OLD}, {"QFMT_VFS_V0", PPM_QFMT_VFS_V0}, {"QFMT_VFS_V1", PPM_QFMT_VFS_V1}, {0, 0}, }; const struct ppm_name_value semop_flags[] = { {"IPC_NOWAIT", PPM_IPC_NOWAIT}, {"SEM_UNDO", PPM_SEM_UNDO}, {0, 0}, }; const struct ppm_name_value semget_flags[] = { {"IPC_EXCL", PPM_IPC_EXCL}, {"IPC_CREAT", PPM_IPC_CREAT}, {0, 0}, }; const struct ppm_name_value semctl_commands[] = { {"IPC_STAT", PPM_IPC_STAT}, {"IPC_SET", PPM_IPC_SET}, {"IPC_RMID", PPM_IPC_RMID}, {"IPC_INFO", PPM_IPC_INFO}, {"SEM_INFO", PPM_SEM_INFO}, {"SEM_STAT", PPM_SEM_STAT}, {"GETALL", PPM_GETALL}, {"GETNCNT", PPM_GETNCNT}, {"GETPID", PPM_GETPID}, {"GETVAL", PPM_GETVAL}, {"GETZCNT", PPM_GETZCNT}, {"SETALL", PPM_SETALL}, {"SETVAL", PPM_SETVAL}, {0, 0}, }; const struct ppm_name_value access_flags[] = { {"F_OK", PPM_F_OK}, {"R_OK", PPM_R_OK}, {"W_OK", PPM_W_OK}, {"X_OK", PPM_X_OK}, {0, 0}, }; const struct ppm_name_value pf_flags[] = { {"PROTECTION_VIOLATION", PPM_PF_PROTECTION_VIOLATION}, {"PAGE_NOT_PRESENT", PPM_PF_PAGE_NOT_PRESENT}, {"WRITE_ACCESS", PPM_PF_WRITE_ACCESS}, {"READ_ACCESS", PPM_PF_READ_ACCESS}, {"USER_FAULT", PPM_PF_USER_FAULT}, {"SUPERVISOR_FAULT", PPM_PF_SUPERVISOR_FAULT}, {"RESERVED_PAGE", PPM_PF_RESERVED_PAGE}, {"INSTRUCTION_FETCH", PPM_PF_INSTRUCTION_FETCH}, {0, 0}, }; const struct ppm_name_value unlinkat_flags[] = { {"AT_REMOVEDIR", PPM_AT_REMOVEDIR}, {0, 0}, }; const struct ppm_name_value linkat_flags[] = { {"AT_SYMLINK_FOLLOW", PPM_AT_SYMLINK_FOLLOW}, {"AT_EMPTY_PATH", PPM_AT_EMPTY_PATH}, {0, 0}, }; const struct ppm_name_value chmod_mode[] = { {"S_IXOTH", PPM_S_IXOTH}, {"S_IWOTH", PPM_S_IWOTH}, {"S_IROTH", PPM_S_IROTH}, {"S_IXGRP", PPM_S_IXGRP}, {"S_IWGRP", PPM_S_IWGRP}, {"S_IRGRP", PPM_S_IRGRP}, {"S_IXUSR", PPM_S_IXUSR}, {"S_IWUSR", PPM_S_IWUSR}, {"S_IRUSR", PPM_S_IRUSR}, {"S_ISVTX", PPM_S_ISVTX}, {"S_ISGID", PPM_S_ISGID}, {"S_ISUID", PPM_S_ISUID}, {0, 0}, };sysdig-0.26.4/driver/main.c000066400000000000000000002102541352731327100155000ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 20) #include #include #include "ppm_syscall.h" #include #else #include #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)) #include #else #include #endif #include #include #include #include #include #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) #include #else #include #include #endif #include #include #include #include #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) #include #else #include #endif #include #include #include "driver_config.h" #include "ppm_ringbuffer.h" #include "ppm_events_public.h" #include "ppm_events.h" #include "ppm.h" #if defined(CONFIG_IA32_EMULATION) && !defined(__NR_ia32_socketcall) #include "ppm_compat_unistd_32.h" #endif MODULE_LICENSE("GPL"); MODULE_AUTHOR("sysdig inc"); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)) #define TRACEPOINT_PROBE_REGISTER(p1, p2) tracepoint_probe_register(p1, p2) #define TRACEPOINT_PROBE_UNREGISTER(p1, p2) tracepoint_probe_unregister(p1, p2) #define TRACEPOINT_PROBE(probe, args...) static void probe(args) #else #define TRACEPOINT_PROBE_REGISTER(p1, p2) tracepoint_probe_register(p1, p2, NULL) #define TRACEPOINT_PROBE_UNREGISTER(p1, p2) tracepoint_probe_unregister(p1, p2, NULL) #define TRACEPOINT_PROBE(probe, args...) static void probe(void *__data, args) #endif #ifndef pgprot_encrypted #define pgprot_encrypted(x) (x) #endif struct ppm_device { dev_t dev; struct cdev cdev; wait_queue_head_t read_queue; }; struct event_data_t { enum ppm_capture_category category; int socketcall_syscall; bool compat; union { struct { struct pt_regs *regs; long id; const enum ppm_syscall_code *cur_g_syscall_code_routing_table; } syscall_data; struct { struct task_struct *sched_prev; struct task_struct *sched_next; } context_data; struct { int sig; struct siginfo *info; struct k_sigaction *ka; } signal_data; struct fault_data_t fault_data; } event_info; }; /* * FORWARD DECLARATIONS */ static int ppm_open(struct inode *inode, struct file *filp); static int ppm_release(struct inode *inode, struct file *filp); static long ppm_ioctl(struct file *f, unsigned int cmd, unsigned long arg); static int ppm_mmap(struct file *filp, struct vm_area_struct *vma); static int record_event_consumer(struct ppm_consumer_t *consumer, enum ppm_event_type event_type, enum syscall_flags drop_flags, struct timespec *ts, struct event_data_t *event_datap); static void record_event_all_consumers(enum ppm_event_type event_type, enum syscall_flags drop_flags, struct event_data_t *event_datap); static int init_ring_buffer(struct ppm_ring_buffer_context *ring); static void free_ring_buffer(struct ppm_ring_buffer_context *ring); static void reset_ring_buffer(struct ppm_ring_buffer_context *ring); #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)) void ppm_task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st); #endif #ifndef CONFIG_HAVE_SYSCALL_TRACEPOINTS #error The kernel must have HAVE_SYSCALL_TRACEPOINTS in order for sysdig to be useful #endif TRACEPOINT_PROBE(syscall_enter_probe, struct pt_regs *regs, long id); TRACEPOINT_PROBE(syscall_exit_probe, struct pt_regs *regs, long ret); TRACEPOINT_PROBE(syscall_procexit_probe, struct task_struct *p); #ifdef CAPTURE_CONTEXT_SWITCHES #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)) TRACEPOINT_PROBE(sched_switch_probe, struct rq *rq, struct task_struct *prev, struct task_struct *next); #elif (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)) TRACEPOINT_PROBE(sched_switch_probe, struct task_struct *prev, struct task_struct *next); #else TRACEPOINT_PROBE(sched_switch_probe, bool preempt, struct task_struct *prev, struct task_struct *next); #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) */ #endif /* CAPTURE_CONTEXT_SWITCHES */ #ifdef CAPTURE_SIGNAL_DELIVERIES TRACEPOINT_PROBE(signal_deliver_probe, int sig, struct siginfo *info, struct k_sigaction *ka); #endif #ifdef CAPTURE_PAGE_FAULTS TRACEPOINT_PROBE(page_fault_probe, unsigned long address, struct pt_regs *regs, unsigned long error_code); #endif DECLARE_BITMAP(g_events_mask, PPM_EVENT_MAX); static struct ppm_device *g_ppm_devs; static struct class *g_ppm_class; static unsigned int g_ppm_numdevs; static int g_ppm_major; bool g_tracers_enabled = false; bool g_simple_mode_enabled = false; static DEFINE_PER_CPU(long, g_n_tracepoint_hit); static const struct file_operations g_ppm_fops = { .open = ppm_open, .release = ppm_release, .mmap = ppm_mmap, .unlocked_ioctl = ppm_ioctl, .owner = THIS_MODULE, }; /* * GLOBALS */ LIST_HEAD(g_consumer_list); static DEFINE_MUTEX(g_consumer_mutex); static bool g_tracepoint_registered; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) static struct tracepoint *tp_sys_enter; static struct tracepoint *tp_sys_exit; #endif static struct tracepoint *tp_sched_process_exit; #ifdef CAPTURE_CONTEXT_SWITCHES static struct tracepoint *tp_sched_switch; #endif #ifdef CAPTURE_SIGNAL_DELIVERIES static struct tracepoint *tp_signal_deliver; #endif #ifdef CAPTURE_PAGE_FAULTS // Even in kernels that can support page fault tracepoints, tracepoints may be // disabled so check if g_fault_tracepoint_disabled is set. static struct tracepoint *tp_page_fault_user; static struct tracepoint *tp_page_fault_kernel; static bool g_fault_tracepoint_registered; static bool g_fault_tracepoint_disabled; #endif #ifdef _DEBUG static bool verbose = 1; #else static bool verbose = 0; #endif static unsigned int max_consumers = 5; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) static enum cpuhp_state hp_state = 0; #endif #define vpr_info(fmt, ...) \ do { \ if (verbose) \ pr_info(fmt, ##__VA_ARGS__); \ } while (0) inline void ppm_syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, unsigned long *args) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0)) syscall_get_arguments(task, regs, 0, 6, args); #else syscall_get_arguments(task, regs, args); #endif } /* compat tracepoint functions */ static int compat_register_trace(void *func, const char *probename, struct tracepoint *tp) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)) return TRACEPOINT_PROBE_REGISTER(probename, func); #else return tracepoint_probe_register(tp, func, NULL); #endif } static void compat_unregister_trace(void *func, const char *probename, struct tracepoint *tp) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)) TRACEPOINT_PROBE_UNREGISTER(probename, func); #else tracepoint_probe_unregister(tp, func, NULL); #endif } static struct ppm_consumer_t *ppm_find_consumer(struct task_struct *consumer_id) { struct ppm_consumer_t *el = NULL; rcu_read_lock(); list_for_each_entry_rcu(el, &g_consumer_list, node) { if (el->consumer_id == consumer_id) { rcu_read_unlock(); return el; } } rcu_read_unlock(); return NULL; } static void check_remove_consumer(struct ppm_consumer_t *consumer, int remove_from_list) { int cpu; int open_rings = 0; for_each_possible_cpu(cpu) { struct ppm_ring_buffer_context *ring = per_cpu_ptr(consumer->ring_buffers, cpu); if (ring && ring->open) ++open_rings; } if (open_rings == 0) { pr_info("deallocating consumer %p\n", consumer->consumer_id); if (remove_from_list) { list_del_rcu(&consumer->node); synchronize_rcu(); } for_each_possible_cpu(cpu) { struct ppm_ring_buffer_context *ring = per_cpu_ptr(consumer->ring_buffers, cpu); free_ring_buffer(ring); } free_percpu(consumer->ring_buffers); vfree(consumer); } } /* * user I/O functions */ static int ppm_open(struct inode *inode, struct file *filp) { int ret; int in_list = false; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) int ring_no = iminor(filp->f_path.dentry->d_inode); #else int ring_no = iminor(filp->f_dentry->d_inode); #endif struct task_struct *consumer_id = current; struct ppm_consumer_t *consumer = NULL; struct ppm_ring_buffer_context *ring = NULL; /* * Tricky: to identify a consumer, attach the thread id * to the newly open file descriptor */ filp->private_data = consumer_id; mutex_lock(&g_consumer_mutex); consumer = ppm_find_consumer(consumer_id); if (!consumer) { unsigned int cpu; unsigned int num_consumers = 0; struct ppm_consumer_t *el = NULL; rcu_read_lock(); list_for_each_entry_rcu(el, &g_consumer_list, node) { ++num_consumers; } rcu_read_unlock(); if (num_consumers >= max_consumers) { pr_err("maximum number of consumers reached\n"); ret = -EBUSY; goto cleanup_open; } pr_info("adding new consumer %p\n", consumer_id); consumer = vmalloc(sizeof(struct ppm_consumer_t)); if (!consumer) { pr_err("can't allocate consumer\n"); ret = -ENOMEM; goto cleanup_open; } consumer->consumer_id = consumer_id; /* * Initialize the ring buffers array */ consumer->ring_buffers = alloc_percpu(struct ppm_ring_buffer_context); if (consumer->ring_buffers == NULL) { pr_err("can't allocate the ring buffer array\n"); vfree(consumer); ret = -ENOMEM; goto cleanup_open; } /* * Note, we have two loops here because the first one makes sure that ALL of the * rings are properly initialized to null, since the second one could be interrupted * and cause issues in the cleanup phase. * This might not be necessary, because alloc_percpu memsets the allocated entries to * 0, but better be extra safe. */ for_each_possible_cpu(cpu) { ring = per_cpu_ptr(consumer->ring_buffers, cpu); ring->cpu_online = false; ring->str_storage = NULL; ring->buffer = NULL; ring->info = NULL; } /* * If a cpu is offline when the consumer is first created, we * will never get events for that cpu even if it later comes * online via hotplug. We could allocate these rings on-demand * later in this function if needed for hotplug, but that * requires the consumer to know to call open again, and sysdig * doesn't support that. */ for_each_online_cpu(cpu) { ring = per_cpu_ptr(consumer->ring_buffers, cpu); pr_info("initializing ring buffer for CPU %u\n", cpu); if (!init_ring_buffer(ring)) { pr_err("can't initialize the ring buffer for CPU %u\n", cpu); ret = -ENOMEM; goto err_init_ring_buffer; } ring->cpu_online = true; } list_add_rcu(&consumer->node, &g_consumer_list); in_list = true; } else { vpr_info("found already existent consumer %p\n", consumer_id); } ring = per_cpu_ptr(consumer->ring_buffers, ring_no); /* * Check if the CPU pointed by this device is online. If it isn't stop here and * return ENODEV. The cpu could be online while buffer is NULL if there's a cpu * online hotplug callback between the first open on this consumer and the open * for this particular device. */ if (ring->cpu_online == false || ring->buffer == NULL) { ret = -ENODEV; goto cleanup_open; } if (ring->open) { pr_err("invalid operation: attempting to open device %d multiple times for consumer %p\n", ring_no, consumer->consumer_id); ret = -EBUSY; goto cleanup_open; } vpr_info("opening ring %d, consumer %p\n", ring_no, consumer->consumer_id); /* * ring->preempt_count is not reset to 0 on purpose, to prevent a race condition: * if the same device is quickly closed and then reopened, record_event() might still be executing * (with ring->preempt_count to 1) while ppm_open() resets ring->preempt_count to 0. * When record_event() will exit, it will decrease * ring->preempt_count which will become < 0, leading to the complete loss of all the events for that CPU. */ consumer->dropping_mode = 0; consumer->snaplen = RW_SNAPLEN; consumer->sampling_ratio = 1; consumer->sampling_interval = 0; consumer->is_dropping = 0; consumer->do_dynamic_snaplen = false; consumer->need_to_insert_drop_e = 0; consumer->need_to_insert_drop_x = 0; consumer->fullcapture_port_range_start = 0; consumer->fullcapture_port_range_end = 0; consumer->statsd_port = PPM_PORT_STATSD; bitmap_fill(g_events_mask, PPM_EVENT_MAX); /* Enable all syscall to be passed to userspace */ reset_ring_buffer(ring); ring->open = true; if (!g_tracepoint_registered) { pr_info("starting capture\n"); /* * Enable the tracepoints */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) ret = compat_register_trace(syscall_exit_probe, "sys_exit", tp_sys_exit); #else ret = register_trace_syscall_exit(syscall_exit_probe); #endif if (ret) { pr_err("can't create the sys_exit tracepoint\n"); goto err_sys_exit; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) ret = compat_register_trace(syscall_enter_probe, "sys_enter", tp_sys_enter); #else ret = register_trace_syscall_enter(syscall_enter_probe); #endif if (ret) { pr_err("can't create the sys_enter tracepoint\n"); goto err_sys_enter; } ret = compat_register_trace(syscall_procexit_probe, "sched_process_exit", tp_sched_process_exit); if (ret) { pr_err("can't create the sched_process_exit tracepoint\n"); goto err_sched_procexit; } #ifdef CAPTURE_CONTEXT_SWITCHES ret = compat_register_trace(sched_switch_probe, "sched_switch", tp_sched_switch); if (ret) { pr_err("can't create the sched_switch tracepoint\n"); goto err_sched_switch; } #endif #ifdef CAPTURE_SIGNAL_DELIVERIES ret = compat_register_trace(signal_deliver_probe, "signal_deliver", tp_signal_deliver); if (ret) { pr_err("can't create the signal_deliver tracepoint\n"); goto err_signal_deliver; } #endif g_tracepoint_registered = true; } ret = 0; goto cleanup_open; #ifdef CAPTURE_SIGNAL_DELIVERIES err_signal_deliver: compat_unregister_trace(sched_switch_probe, "sched_switch", tp_sched_switch); #endif err_sched_switch: compat_unregister_trace(syscall_procexit_probe, "sched_process_exit", tp_sched_process_exit); err_sched_procexit: #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) compat_unregister_trace(syscall_enter_probe, "sys_enter", tp_sys_enter); #else unregister_trace_syscall_enter(syscall_enter_probe); #endif err_sys_enter: #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) compat_unregister_trace(syscall_exit_probe, "sys_exit", tp_sys_exit); #else unregister_trace_syscall_exit(syscall_exit_probe); #endif err_sys_exit: ring->open = false; err_init_ring_buffer: check_remove_consumer(consumer, in_list); cleanup_open: mutex_unlock(&g_consumer_mutex); return ret; } static int ppm_release(struct inode *inode, struct file *filp) { int cpu; int ret; struct ppm_ring_buffer_context *ring; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) int ring_no = iminor(filp->f_path.dentry->d_inode); #else int ring_no = iminor(filp->f_dentry->d_inode); #endif struct task_struct *consumer_id = filp->private_data; struct ppm_consumer_t *consumer = NULL; mutex_lock(&g_consumer_mutex); consumer = ppm_find_consumer(consumer_id); if (!consumer) { pr_err("release: unknown consumer %p\n", consumer_id); ret = -EBUSY; goto cleanup_release; } ring = per_cpu_ptr(consumer->ring_buffers, ring_no); if (!ring) { ASSERT(false); ret = -ENODEV; goto cleanup_release; } if (!ring->open) { pr_err("attempting to close unopened device %d for consumer %p\n", ring_no, consumer_id); ret = -EBUSY; goto cleanup_release; } ring->capture_enabled = false; vpr_info("closing ring %d, consumer:%p evt:%llu, dr_buf:%llu, dr_pf:%llu, pr:%llu, cs:%llu\n", ring_no, consumer_id, ring->info->n_evts, ring->info->n_drops_buffer, ring->info->n_drops_pf, ring->info->n_preemptions, ring->info->n_context_switches); ring->open = false; check_remove_consumer(consumer, true); /* * The last closed device stops event collection */ if (list_empty(&g_consumer_list)) { if (g_tracepoint_registered) { pr_info("no more consumers, stopping capture\n"); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) compat_unregister_trace(syscall_exit_probe, "sys_exit", tp_sys_exit); compat_unregister_trace(syscall_enter_probe, "sys_enter", tp_sys_enter); #else unregister_trace_syscall_exit(syscall_exit_probe); unregister_trace_syscall_enter(syscall_enter_probe); #endif compat_unregister_trace(syscall_procexit_probe, "sched_process_exit", tp_sched_process_exit); #ifdef CAPTURE_CONTEXT_SWITCHES compat_unregister_trace(sched_switch_probe, "sched_switch", tp_sched_switch); #endif #ifdef CAPTURE_SIGNAL_DELIVERIES compat_unregister_trace(signal_deliver_probe, "signal_deliver", tp_signal_deliver); #endif #ifdef CAPTURE_PAGE_FAULTS if (g_fault_tracepoint_registered) { compat_unregister_trace(page_fault_probe, "page_fault_user", tp_page_fault_user); compat_unregister_trace(page_fault_probe, "page_fault_kernel", tp_page_fault_kernel); g_fault_tracepoint_registered = false; } #endif #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) tracepoint_synchronize_unregister(); #endif g_tracepoint_registered = false; /* * While we're here, disable simple mode if it's active */ g_simple_mode_enabled = false; /* * Reset tracepoint counter */ for_each_possible_cpu(cpu) { per_cpu(g_n_tracepoint_hit, cpu) = 0; } } else { ASSERT(false); } } ret = 0; cleanup_release: mutex_unlock(&g_consumer_mutex); return ret; } static long ppm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int cpu; int ret; struct task_struct *consumer_id = filp->private_data; struct ppm_consumer_t *consumer = NULL; if (cmd == PPM_IOCTL_GET_PROCLIST) { struct ppm_proclist_info *proclist_info = NULL; struct task_struct *p, *t; u64 nentries = 0; struct ppm_proclist_info pli; u32 memsize; if (copy_from_user(&pli, (void *)arg, sizeof(pli))) { ret = -EINVAL; goto cleanup_ioctl_nolock; } vpr_info("PPM_IOCTL_GET_PROCLIST, size=%d\n", (int)pli.max_entries); memsize = sizeof(struct ppm_proclist_info) + sizeof(struct ppm_proc_info) * pli.max_entries; proclist_info = vmalloc(memsize); if (!proclist_info) { ret = -EINVAL; goto cleanup_ioctl_nolock; } proclist_info->max_entries = pli.max_entries; rcu_read_lock(); #ifdef for_each_process_thread for_each_process_thread(p, t) { #else #ifdef for_each_process_all for_each_process_all(p) { #else for_each_process(p) { #endif t = p; do { task_lock(p); #endif if (nentries < pli.max_entries) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) cputime_t utime, stime; #else u64 utime, stime; #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) task_cputime_adjusted(t, &utime, &stime); #else ppm_task_cputime_adjusted(t, &utime, &stime); #endif proclist_info->entries[nentries].pid = t->pid; #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) proclist_info->entries[nentries].utime = cputime_to_clock_t(utime); proclist_info->entries[nentries].stime = cputime_to_clock_t(stime); #else proclist_info->entries[nentries].utime = nsec_to_clock_t(utime); proclist_info->entries[nentries].stime = nsec_to_clock_t(stime); #endif } nentries++; #ifdef for_each_process_thread } #else task_unlock(p); #ifdef while_each_thread_all } while_each_thread_all(p, t); } #else } while_each_thread(p, t); } #endif #endif rcu_read_unlock(); proclist_info->n_entries = nentries; if (nentries >= pli.max_entries) { vpr_info("PPM_IOCTL_GET_PROCLIST: not enough space (%d avail, %d required)\n", (int)pli.max_entries, (int)nentries); if (copy_to_user((void *)arg, proclist_info, sizeof(struct ppm_proclist_info))) { ret = -EINVAL; goto cleanup_ioctl_procinfo; } ret = -ENOSPC; goto cleanup_ioctl_procinfo; } else { memsize = sizeof(struct ppm_proclist_info) + sizeof(struct ppm_proc_info) * nentries; if (copy_to_user((void *)arg, proclist_info, memsize)) { ret = -EINVAL; goto cleanup_ioctl_procinfo; } } ret = 0; cleanup_ioctl_procinfo: vfree((void *)proclist_info); goto cleanup_ioctl_nolock; } if (cmd == PPM_IOCTL_GET_N_TRACEPOINT_HIT) { long __user *counters = (long __user *) arg; for_each_possible_cpu(cpu) { if (put_user(per_cpu(g_n_tracepoint_hit, cpu), &counters[cpu])) { ret = -EINVAL; goto cleanup_ioctl_nolock; } } ret = 0; goto cleanup_ioctl_nolock; } else if (cmd == PPM_IOCTL_GET_PROBE_VERSION) { if (copy_to_user((void *)arg, PROBE_VERSION, sizeof(PROBE_VERSION))) { ret = -EINVAL; goto cleanup_ioctl_nolock; } ret = 0; goto cleanup_ioctl_nolock; } mutex_lock(&g_consumer_mutex); consumer = ppm_find_consumer(consumer_id); if (!consumer) { pr_err("ioctl: unknown consumer %p\n", consumer_id); ret = -EBUSY; goto cleanup_ioctl; } switch (cmd) { case PPM_IOCTL_DISABLE_CAPTURE: { #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) int ring_no = iminor(filp->f_path.dentry->d_inode); #else int ring_no = iminor(filp->f_dentry->d_inode); #endif struct ppm_ring_buffer_context *ring = per_cpu_ptr(consumer->ring_buffers, ring_no); if (!ring) { ASSERT(false); ret = -ENODEV; goto cleanup_ioctl; } ring->capture_enabled = false; vpr_info("PPM_IOCTL_DISABLE_CAPTURE for ring %d, consumer %p\n", ring_no, consumer_id); ret = 0; goto cleanup_ioctl; } case PPM_IOCTL_ENABLE_CAPTURE: { #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) int ring_no = iminor(filp->f_path.dentry->d_inode); #else int ring_no = iminor(filp->f_dentry->d_inode); #endif struct ppm_ring_buffer_context *ring = per_cpu_ptr(consumer->ring_buffers, ring_no); if (!ring) { ASSERT(false); ret = -ENODEV; goto cleanup_ioctl; } ring->capture_enabled = true; vpr_info("PPM_IOCTL_ENABLE_CAPTURE for ring %d, consumer %p\n", ring_no, consumer_id); ret = 0; goto cleanup_ioctl; } case PPM_IOCTL_DISABLE_DROPPING_MODE: { struct event_data_t event_data; struct timespec ts; vpr_info("PPM_IOCTL_DISABLE_DROPPING_MODE, consumer %p\n", consumer_id); consumer->dropping_mode = 0; consumer->sampling_interval = 1000000000; consumer->sampling_ratio = 1; /* * Push an event into the ring buffer so that the user can know that dropping * mode has been disabled */ getnstimeofday(&ts); event_data.category = PPMC_CONTEXT_SWITCH; event_data.event_info.context_data.sched_prev = (void *)DEI_DISABLE_DROPPING; event_data.event_info.context_data.sched_next = (void *)0; record_event_consumer(consumer, PPME_SYSDIGEVENT_E, UF_NEVER_DROP, &ts, &event_data); ret = 0; goto cleanup_ioctl; } case PPM_IOCTL_ENABLE_DROPPING_MODE: { u32 new_sampling_ratio; consumer->dropping_mode = 1; vpr_info("PPM_IOCTL_ENABLE_DROPPING_MODE, consumer %p\n", consumer_id); new_sampling_ratio = (u32)arg; if (new_sampling_ratio != 1 && new_sampling_ratio != 2 && new_sampling_ratio != 4 && new_sampling_ratio != 8 && new_sampling_ratio != 16 && new_sampling_ratio != 32 && new_sampling_ratio != 64 && new_sampling_ratio != 128) { pr_err("invalid sampling ratio %u\n", new_sampling_ratio); ret = -EINVAL; goto cleanup_ioctl; } consumer->sampling_interval = 1000000000 / new_sampling_ratio; consumer->sampling_ratio = new_sampling_ratio; vpr_info("new sampling ratio: %d\n", new_sampling_ratio); ret = 0; goto cleanup_ioctl; } case PPM_IOCTL_SET_SNAPLEN: { u32 new_snaplen; vpr_info("PPM_IOCTL_SET_SNAPLEN, consumer %p\n", consumer_id); new_snaplen = (u32)arg; if (new_snaplen > RW_MAX_SNAPLEN) { pr_err("invalid snaplen %u\n", new_snaplen); ret = -EINVAL; goto cleanup_ioctl; } consumer->snaplen = new_snaplen; vpr_info("new snaplen: %d\n", consumer->snaplen); ret = 0; goto cleanup_ioctl; } case PPM_IOCTL_SET_FULLCAPTURE_PORT_RANGE: { u32 encoded_port_range; vpr_info("PPM_IOCTL_SET_FULLCAPTURE_PORT_RANGE, consumer %p\n", consumer_id); encoded_port_range = (u32)arg; consumer->fullcapture_port_range_start = encoded_port_range & 0xFFFF; consumer->fullcapture_port_range_end = encoded_port_range >> 16; pr_info("new fullcapture_port_range_start: %d\n", (int)consumer->fullcapture_port_range_start); pr_info("new fullcapture_port_range_end: %d\n", (int)consumer->fullcapture_port_range_end); ret = 0; goto cleanup_ioctl; } case PPM_IOCTL_SET_STATSD_PORT: { consumer->statsd_port = (u16)arg; pr_info("new statsd_port: %d\n", (int)consumer->statsd_port); ret = 0; goto cleanup_ioctl; } case PPM_IOCTL_MASK_ZERO_EVENTS: { vpr_info("PPM_IOCTL_MASK_ZERO_EVENTS, consumer %p\n", consumer_id); bitmap_zero(g_events_mask, PPM_EVENT_MAX); /* Used for dropping events so they must stay on */ set_bit(PPME_DROP_E, g_events_mask); set_bit(PPME_DROP_X, g_events_mask); ret = 0; goto cleanup_ioctl; } case PPM_IOCTL_MASK_SET_EVENT: { u32 syscall_to_set = (u32)arg; vpr_info("PPM_IOCTL_MASK_SET_EVENT (%u), consumer %p\n", syscall_to_set, consumer_id); if (syscall_to_set >= PPM_EVENT_MAX) { pr_err("invalid syscall %u\n", syscall_to_set); ret = -EINVAL; goto cleanup_ioctl; } set_bit(syscall_to_set, g_events_mask); ret = 0; goto cleanup_ioctl; } case PPM_IOCTL_MASK_UNSET_EVENT: { u32 syscall_to_unset = (u32)arg; vpr_info("PPM_IOCTL_MASK_UNSET_EVENT (%u), consumer %p\n", syscall_to_unset, consumer_id); if (syscall_to_unset >= PPM_EVENT_MAX) { pr_err("invalid syscall %u\n", syscall_to_unset); ret = -EINVAL; goto cleanup_ioctl; } clear_bit(syscall_to_unset, g_events_mask); ret = 0; goto cleanup_ioctl; } case PPM_IOCTL_DISABLE_DYNAMIC_SNAPLEN: { consumer->do_dynamic_snaplen = false; ret = 0; goto cleanup_ioctl; } case PPM_IOCTL_ENABLE_DYNAMIC_SNAPLEN: { consumer->do_dynamic_snaplen = true; ret = 0; goto cleanup_ioctl; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) case PPM_IOCTL_GET_VTID: case PPM_IOCTL_GET_VPID: { pid_t vid; struct pid *pid; struct task_struct *task; struct pid_namespace *ns; rcu_read_lock(); pid = find_pid_ns(arg, &init_pid_ns); if (!pid) { rcu_read_unlock(); ret = -EINVAL; goto cleanup_ioctl; } task = pid_task(pid, PIDTYPE_PID); if (!task) { rcu_read_unlock(); ret = -EINVAL; goto cleanup_ioctl; } ns = ns_of_pid(pid); if (!pid) { rcu_read_unlock(); ret = -EINVAL; goto cleanup_ioctl; } if (cmd == PPM_IOCTL_GET_VTID) vid = task_pid_nr_ns(task, ns); else vid = task_tgid_nr_ns(task, ns); rcu_read_unlock(); ret = vid; goto cleanup_ioctl; } #endif #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) case PPM_IOCTL_GET_CURRENT_TID: ret = task_pid_nr(current); goto cleanup_ioctl; case PPM_IOCTL_GET_CURRENT_PID: ret = task_tgid_nr(current); goto cleanup_ioctl; #endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) */ #ifdef CAPTURE_SIGNAL_DELIVERIES case PPM_IOCTL_DISABLE_SIGNAL_DELIVER: { vpr_info("PPM_IOCTL_DISABLE_SIGNAL_DELIVER\n"); if (g_tracepoint_registered) compat_unregister_trace(signal_deliver_probe, "signal_deliver", tp_signal_deliver); ret = 0; goto cleanup_ioctl; } case PPM_IOCTL_ENABLE_SIGNAL_DELIVER: { vpr_info("PPM_IOCTL_ENABLE_SIGNAL_DELIVER\n"); if (g_tracepoint_registered) compat_register_trace(signal_deliver_probe, "signal_deliver", tp_signal_deliver); ret = 0; goto cleanup_ioctl; } #endif case PPM_IOCTL_SET_TRACERS_CAPTURE: { vpr_info("PPM_IOCTL_SET_TRACERS_CAPTURE, consumer %p\n", consumer_id); g_tracers_enabled = true; ret = 0; goto cleanup_ioctl; } case PPM_IOCTL_SET_SIMPLE_MODE: { vpr_info("PPM_IOCTL_SET_SIMPLE_MODE, consumer %p\n", consumer_id); g_simple_mode_enabled = true; ret = 0; goto cleanup_ioctl; } case PPM_IOCTL_ENABLE_PAGE_FAULTS: { vpr_info("PPM_IOCTL_ENABLE_PAGE_FAULTS\n"); #ifdef CAPTURE_PAGE_FAULTS ASSERT(g_tracepoint_registered); if (g_fault_tracepoint_disabled) { pr_err("kernel page fault tracepoints are disabled\n"); ret = -EPERM; goto cleanup_ioctl; } if (!g_fault_tracepoint_registered) { ret = compat_register_trace(page_fault_probe, "page_fault_user", tp_page_fault_user); if (ret) { pr_err("can't create the page_fault_user tracepoint\n"); ret = -EINVAL; goto cleanup_ioctl; } ret = compat_register_trace(page_fault_probe, "page_fault_kernel", tp_page_fault_kernel); if (ret) { pr_err("can't create the page_fault_kernel tracepoint\n"); ret = -EINVAL; goto err_page_fault_kernel; } g_fault_tracepoint_registered = true; } ret = 0; goto cleanup_ioctl; #else pr_err("kernel doesn't support page fault tracepoints\n"); ret = -EINVAL; goto cleanup_ioctl; #endif } default: ret = -ENOTTY; goto cleanup_ioctl; } #ifdef CAPTURE_PAGE_FAULTS err_page_fault_kernel: compat_unregister_trace(page_fault_probe, "page_fault_user", tp_page_fault_user); #endif cleanup_ioctl: mutex_unlock(&g_consumer_mutex); cleanup_ioctl_nolock: return ret; } static int ppm_mmap(struct file *filp, struct vm_area_struct *vma) { int ret; struct task_struct *consumer_id = filp->private_data; struct ppm_consumer_t *consumer = NULL; mutex_lock(&g_consumer_mutex); consumer = ppm_find_consumer(consumer_id); if (!consumer) { pr_err("mmap: unknown consumer %p\n", consumer_id); ret = -EIO; goto cleanup_mmap; } if (vma->vm_pgoff == 0) { long length = vma->vm_end - vma->vm_start; unsigned long useraddr = vma->vm_start; unsigned long pfn; char *vmalloc_area_ptr; char *orig_vmalloc_area_ptr; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) int ring_no = iminor(filp->f_path.dentry->d_inode); #else int ring_no = iminor(filp->f_dentry->d_inode); #endif struct ppm_ring_buffer_context *ring; vpr_info("mmap for consumer %p, CPU %d, start=%lu len=%ld page_size=%lu\n", consumer_id, ring_no, useraddr, length, PAGE_SIZE); /* * Enforce ring buffer size */ if (RING_BUF_SIZE < 2 * PAGE_SIZE) { pr_err("Ring buffer size too small (%ld bytes, must be at least %ld bytes\n", (long)RING_BUF_SIZE, (long)PAGE_SIZE); ret = -EIO; goto cleanup_mmap; } if (RING_BUF_SIZE / PAGE_SIZE * PAGE_SIZE != RING_BUF_SIZE) { pr_err("Ring buffer size is not a multiple of the page size\n"); ret = -EIO; goto cleanup_mmap; } /* * Retrieve the ring structure for this CPU */ ring = per_cpu_ptr(consumer->ring_buffers, ring_no); if (!ring) { ASSERT(false); ret = -ENODEV; goto cleanup_mmap; } if (length <= PAGE_SIZE) { /* * When the size requested by the user is smaller than a page, we assume * she's mapping the ring info structure */ vpr_info("mapping the ring info\n"); vmalloc_area_ptr = (char *)ring->info; orig_vmalloc_area_ptr = vmalloc_area_ptr; pfn = vmalloc_to_pfn(vmalloc_area_ptr); ret = remap_pfn_range(vma, useraddr, pfn, PAGE_SIZE, pgprot_encrypted(PAGE_SHARED)); if (ret < 0) { pr_err("remap_pfn_range failed (1)\n"); goto cleanup_mmap; } ret = 0; goto cleanup_mmap; } else if (length == RING_BUF_SIZE * 2) { long mlength; /* * When the size requested by the user equals the ring buffer size, we map the full * buffer */ vpr_info("mapping the data buffer\n"); vmalloc_area_ptr = (char *)ring->buffer; orig_vmalloc_area_ptr = vmalloc_area_ptr; /* * Validate that the buffer access is read only */ if (vma->vm_flags & VM_WRITE) { pr_err("invalid mmap flags 0x%lx\n", vma->vm_flags); ret = -EIO; goto cleanup_mmap; } /* * Map each single page of the buffer */ mlength = length / 2; while (mlength > 0) { pfn = vmalloc_to_pfn(vmalloc_area_ptr); ret = remap_pfn_range(vma, useraddr, pfn, PAGE_SIZE, pgprot_encrypted(PAGE_SHARED)); if (ret < 0) { pr_err("remap_pfn_range failed (1)\n"); goto cleanup_mmap; } useraddr += PAGE_SIZE; vmalloc_area_ptr += PAGE_SIZE; mlength -= PAGE_SIZE; } /* * Remap a second copy of the buffer pages at the end of the buffer. * This effectively mirrors the buffer at its end and helps simplify buffer management in userland. */ vmalloc_area_ptr = orig_vmalloc_area_ptr; mlength = length / 2; while (mlength > 0) { pfn = vmalloc_to_pfn(vmalloc_area_ptr); ret = remap_pfn_range(vma, useraddr, pfn, PAGE_SIZE, pgprot_encrypted(PAGE_SHARED)); if (ret < 0) { pr_err("remap_pfn_range failed (1)\n"); goto cleanup_mmap; } useraddr += PAGE_SIZE; vmalloc_area_ptr += PAGE_SIZE; mlength -= PAGE_SIZE; } ret = 0; goto cleanup_mmap; } pr_err("Invalid mmap size %ld\n", length); ret = -EIO; goto cleanup_mmap; } pr_err("invalid pgoff %lu, must be 0\n", vma->vm_pgoff); ret = -EIO; cleanup_mmap: mutex_unlock(&g_consumer_mutex); return ret; } /* Argument list sizes for sys_socketcall */ #define AL(x) ((x) * sizeof(unsigned long)) static const unsigned char nas[21] = { AL(0), AL(3), AL(3), AL(3), AL(2), AL(3), AL(3), AL(3), AL(4), AL(4), AL(4), AL(6), AL(6), AL(2), AL(5), AL(5), AL(3), AL(3), AL(4), AL(5), AL(4) }; #undef AL #ifdef CONFIG_COMPAT #define AL(x) ((x) * sizeof(compat_ulong_t)) static const unsigned char compat_nas[21] = { AL(0), AL(3), AL(3), AL(3), AL(2), AL(3), AL(3), AL(3), AL(4), AL(4), AL(4), AL(6), AL(6), AL(2), AL(5), AL(5), AL(3), AL(3), AL(4), AL(5), AL(4) }; #undef AL #endif #ifdef _HAS_SOCKETCALL static enum ppm_event_type parse_socketcall(struct event_filler_arguments *filler_args, struct pt_regs *regs) { unsigned long __user args[6] = {}; unsigned long __user *scargs; int socketcall_id; ppm_syscall_get_arguments(current, regs, args); socketcall_id = args[0]; scargs = (unsigned long __user *)args[1]; if (unlikely(socketcall_id < SYS_SOCKET || #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) socketcall_id > SYS_SENDMMSG)) #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) socketcall_id > SYS_RECVMMSG)) #else socketcall_id > SYS_ACCEPT4)) #endif return PPME_GENERIC_E; #ifdef CONFIG_COMPAT if (unlikely(filler_args->compat)) { compat_ulong_t socketcall_args32[6]; int j; if (unlikely(ppm_copy_from_user(socketcall_args32, compat_ptr(args[1]), compat_nas[socketcall_id]))) return PPME_GENERIC_E; for (j = 0; j < 6; ++j) filler_args->socketcall_args[j] = (unsigned long)socketcall_args32[j]; } else { #endif if (unlikely(ppm_copy_from_user(filler_args->socketcall_args, scargs, nas[socketcall_id]))) return PPME_GENERIC_E; #ifdef CONFIG_COMPAT } #endif switch (socketcall_id) { case SYS_SOCKET: return PPME_SOCKET_SOCKET_E; case SYS_BIND: return PPME_SOCKET_BIND_E; case SYS_CONNECT: return PPME_SOCKET_CONNECT_E; case SYS_LISTEN: return PPME_SOCKET_LISTEN_E; case SYS_ACCEPT: return PPME_SOCKET_ACCEPT_5_E; case SYS_GETSOCKNAME: return PPME_SOCKET_GETSOCKNAME_E; case SYS_GETPEERNAME: return PPME_SOCKET_GETPEERNAME_E; case SYS_SOCKETPAIR: return PPME_SOCKET_SOCKETPAIR_E; case SYS_SEND: return PPME_SOCKET_SEND_E; case SYS_SENDTO: return PPME_SOCKET_SENDTO_E; case SYS_RECV: return PPME_SOCKET_RECV_E; case SYS_RECVFROM: return PPME_SOCKET_RECVFROM_E; case SYS_SHUTDOWN: return PPME_SOCKET_SHUTDOWN_E; case SYS_SETSOCKOPT: return PPME_SOCKET_SETSOCKOPT_E; case SYS_GETSOCKOPT: return PPME_SOCKET_GETSOCKOPT_E; case SYS_SENDMSG: return PPME_SOCKET_SENDMSG_E; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) case SYS_SENDMMSG: return PPME_SOCKET_SENDMMSG_E; #endif case SYS_RECVMSG: return PPME_SOCKET_RECVMSG_E; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) case SYS_RECVMMSG: return PPME_SOCKET_RECVMMSG_E; #endif case SYS_ACCEPT4: return PPME_SOCKET_ACCEPT4_5_E; default: ASSERT(false); return PPME_GENERIC_E; } } #endif /* _HAS_SOCKETCALL */ static inline void record_drop_e(struct ppm_consumer_t *consumer, struct timespec *ts) { struct event_data_t event_data = {0}; if (record_event_consumer(consumer, PPME_DROP_E, UF_NEVER_DROP, ts, &event_data) == 0) { consumer->need_to_insert_drop_e = 1; } else { if (consumer->need_to_insert_drop_e == 1) pr_err("drop enter event delayed insert\n"); consumer->need_to_insert_drop_e = 0; } } static inline void record_drop_x(struct ppm_consumer_t *consumer, struct timespec *ts) { struct event_data_t event_data = {0}; if (record_event_consumer(consumer, PPME_DROP_X, UF_NEVER_DROP, ts, &event_data) == 0) { consumer->need_to_insert_drop_x = 1; } else { if (consumer->need_to_insert_drop_x == 1) pr_err("drop exit event delayed insert\n"); consumer->need_to_insert_drop_x = 0; } } // Return 1 if the event should be dropped, else 0 static inline int drop_nostate_event(enum ppm_event_type event_type, struct pt_regs *regs) { unsigned long args[6] = {}; unsigned long arg = 0; int close_fd = -1; struct files_struct *files; struct fdtable *fdt; bool drop = false; switch (event_type) { case PPME_SYSCALL_CLOSE_X: case PPME_SOCKET_BIND_X: if (syscall_get_return_value(current, regs) < 0) drop = true; break; case PPME_SYSCALL_CLOSE_E: /* * It's annoying but valid for a program to make a large number of * close() calls on nonexistent fds. That can cause driver cpu usage * to spike dramatically, so drop close events if the fd is not valid. * * The invalid fd events don't matter to userspace in dropping mode, * so we do this before the UF_NEVER_DROP check */ ppm_syscall_get_arguments(current, regs, args); arg = args[0]; close_fd = (int)arg; files = current->files; spin_lock(&files->file_lock); fdt = files_fdtable(files); if (close_fd < 0 || close_fd >= fdt->max_fds || #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) !FD_ISSET(close_fd, fdt->open_fds) #else !fd_is_open(close_fd, fdt) #endif ) { drop = true; } spin_unlock(&files->file_lock); break; case PPME_SYSCALL_FCNTL_E: case PPME_SYSCALL_FCNTL_X: // cmd arg ppm_syscall_get_arguments(current, regs, args); arg = args[1]; if (arg != F_DUPFD && arg != F_DUPFD_CLOEXEC) drop = true; break; default: break; } if (drop) return 1; else return 0; } // Return 1 if the event should be dropped, else 0 static inline int drop_event(struct ppm_consumer_t *consumer, enum ppm_event_type event_type, enum syscall_flags drop_flags, struct timespec *ts, struct pt_regs *regs) { int maybe_ret = 0; if (consumer->dropping_mode) { maybe_ret = drop_nostate_event(event_type, regs); if (maybe_ret > 0) return maybe_ret; } if (drop_flags & UF_NEVER_DROP) { ASSERT((drop_flags & UF_ALWAYS_DROP) == 0); return 0; } if (consumer->dropping_mode) { if (drop_flags & UF_ALWAYS_DROP) { ASSERT((drop_flags & UF_NEVER_DROP) == 0); return 1; } if (ts->tv_nsec >= consumer->sampling_interval) { if (consumer->is_dropping == 0) { consumer->is_dropping = 1; record_drop_e(consumer, ts); } return 1; } if (consumer->is_dropping == 1) { consumer->is_dropping = 0; record_drop_x(consumer, ts); } } return 0; } static void record_event_all_consumers(enum ppm_event_type event_type, enum syscall_flags drop_flags, struct event_data_t *event_datap) { struct ppm_consumer_t *consumer; struct timespec ts; getnstimeofday(&ts); rcu_read_lock(); list_for_each_entry_rcu(consumer, &g_consumer_list, node) { record_event_consumer(consumer, event_type, drop_flags, &ts, event_datap); } rcu_read_unlock(); } /* * Returns 0 if the event is dropped */ static int record_event_consumer(struct ppm_consumer_t *consumer, enum ppm_event_type event_type, enum syscall_flags drop_flags, struct timespec *ts, struct event_data_t *event_datap) { int res = 0; size_t event_size = 0; int next; u32 freespace; u32 usedspace; u32 delta_from_end; struct event_filler_arguments args; u32 ttail; u32 head; struct ppm_ring_buffer_context *ring; struct ppm_ring_buffer_info *ring_info; int drop = 1; int32_t cbres = PPM_SUCCESS; int cpu; if (!test_bit(event_type, g_events_mask)) return res; if (event_type != PPME_DROP_E && event_type != PPME_DROP_X) { if (consumer->need_to_insert_drop_e == 1) record_drop_e(consumer, ts); else if (consumer->need_to_insert_drop_x == 1) record_drop_x(consumer, ts); if (drop_event(consumer, event_type, drop_flags, ts, event_datap->event_info.syscall_data.regs)) return res; } /* * FROM THIS MOMENT ON, WE HAVE TO BE SUPER FAST */ cpu = get_cpu(); ring = per_cpu_ptr(consumer->ring_buffers, cpu); ASSERT(ring); ring_info = ring->info; if (!ring->capture_enabled) { put_cpu(); return res; } ring_info->n_evts++; if (event_datap->category == PPMC_CONTEXT_SWITCH && event_datap->event_info.context_data.sched_prev != NULL) { if (event_type != PPME_SYSDIGEVENT_E && event_type != PPME_CPU_HOTPLUG_E) { ASSERT(event_datap->event_info.context_data.sched_prev != NULL); ASSERT(event_datap->event_info.context_data.sched_next != NULL); ring_info->n_context_switches++; } } /* * Preemption gate */ if (unlikely(atomic_inc_return(&ring->preempt_count) != 1)) { /* When this driver executing a filler calls ppm_copy_from_user(), * even if the page fault is disabled, the page fault tracepoint gets * called very early in the page fault handler, way before the kernel * terminates it, so this is legit. Still not sure how to solve this, * so for the moment handle this case by not complaining and ignoring * the false alarm if the preemption exception is generated by * page_fault_kernel. The alternative would be to disable the kernel * tracepoint completely, but there is value in seeing page faults * generated on this side, so let's see if someone complains. * This means that effectively those events would be lost. */ if (event_type != PPME_PAGE_FAULT_E) { ring_info->n_preemptions++; ASSERT(false); } atomic_dec(&ring->preempt_count); put_cpu(); return res; } /* * Calculate the space currently available in the buffer */ head = ring_info->head; ttail = ring_info->tail; if (ttail > head) freespace = ttail - head - 1; else freespace = RING_BUF_SIZE + ttail - head - 1; usedspace = RING_BUF_SIZE - freespace - 1; delta_from_end = RING_BUF_SIZE + (2 * PAGE_SIZE) - head - 1; ASSERT(freespace <= RING_BUF_SIZE); ASSERT(usedspace <= RING_BUF_SIZE); ASSERT(ttail <= RING_BUF_SIZE); ASSERT(head <= RING_BUF_SIZE); ASSERT(delta_from_end < RING_BUF_SIZE + (2 * PAGE_SIZE)); ASSERT(delta_from_end > (2 * PAGE_SIZE) - 1); #ifdef _HAS_SOCKETCALL /* * If this is a socketcall system call, determine the correct event type * by parsing the arguments and patch event_type accordingly * A bit of explanation: most linux architectures don't have a separate * syscall for each of the socket functions (bind, connect...). Instead, * the socket functions are aggregated into a single syscall, called * socketcall. The first socketcall argument is the call type, while the * second argument contains a pointer to the arguments of the original * call. I guess this was done to reduce the number of syscalls... */ if (event_datap->category == PPMC_SYSCALL && event_datap->event_info.syscall_data.regs && event_datap->event_info.syscall_data.id == event_datap->socketcall_syscall) { enum ppm_event_type tet; args.is_socketcall = true; args.compat = event_datap->compat; tet = parse_socketcall(&args, event_datap->event_info.syscall_data.regs); if (event_type == PPME_GENERIC_E) event_type = tet; else event_type = tet + 1; } else { args.is_socketcall = false; args.compat = false; } args.socketcall_syscall = event_datap->socketcall_syscall; #endif ASSERT(event_type < PPM_EVENT_MAX); /* * Determine how many arguments this event has */ args.nargs = g_event_info[event_type].nparams; args.arg_data_offset = args.nargs * sizeof(u16); /* * Make sure we have enough space for the event header. * We need at least space for the header plus 16 bit per parameter for the lengths. */ if (likely(freespace >= sizeof(struct ppm_evt_hdr) + args.arg_data_offset)) { /* * Populate the header */ struct ppm_evt_hdr *hdr = (struct ppm_evt_hdr *)(ring->buffer + head); #ifdef PPM_ENABLE_SENTINEL hdr->sentinel_begin = ring->nevents; #endif hdr->ts = timespec_to_ns(ts); hdr->tid = current->pid; hdr->type = event_type; hdr->nparams = args.nargs; /* * Populate the parameters for the filler callback */ args.consumer = consumer; args.buffer = ring->buffer + head + sizeof(struct ppm_evt_hdr); #ifdef PPM_ENABLE_SENTINEL args.sentinel = ring->nevents; #endif args.buffer_size = min(freespace, delta_from_end) - sizeof(struct ppm_evt_hdr); /* freespace is guaranteed to be bigger than sizeof(struct ppm_evt_hdr) */ args.event_type = event_type; if (event_datap->category == PPMC_SYSCALL) { args.regs = event_datap->event_info.syscall_data.regs; args.syscall_id = event_datap->event_info.syscall_data.id; args.cur_g_syscall_code_routing_table = event_datap->event_info.syscall_data.cur_g_syscall_code_routing_table; args.compat = event_datap->compat; } else { args.regs = NULL; args.syscall_id = -1; args.cur_g_syscall_code_routing_table = NULL; args.compat = false; } if (event_datap->category == PPMC_CONTEXT_SWITCH) { args.sched_prev = event_datap->event_info.context_data.sched_prev; args.sched_next = event_datap->event_info.context_data.sched_next; } else { args.sched_prev = NULL; args.sched_next = NULL; } if (event_datap->category == PPMC_SIGNAL) { args.signo = event_datap->event_info.signal_data.sig; if (event_datap->event_info.signal_data.info == NULL) { args.spid = (__kernel_pid_t) 0; } else if (args.signo == SIGKILL) { args.spid = event_datap->event_info.signal_data.info->_sifields._kill._pid; } else if (args.signo == SIGTERM || args.signo == SIGHUP || args.signo == SIGINT || args.signo == SIGTSTP || args.signo == SIGQUIT) { if (event_datap->event_info.signal_data.info->si_code == SI_USER || event_datap->event_info.signal_data.info->si_code == SI_QUEUE || event_datap->event_info.signal_data.info->si_code <= 0) { args.spid = event_datap->event_info.signal_data.info->si_pid; } } else if (args.signo == SIGCHLD) { args.spid = event_datap->event_info.signal_data.info->_sifields._sigchld._pid; } else if (args.signo >= SIGRTMIN && args.signo <= SIGRTMAX) { args.spid = event_datap->event_info.signal_data.info->_sifields._rt._pid; } else { args.spid = (__kernel_pid_t) 0; } } else { args.signo = 0; args.spid = (__kernel_pid_t) 0; } args.dpid = current->pid; if (event_datap->category == PPMC_PAGE_FAULT) args.fault_data = event_datap->event_info.fault_data; args.curarg = 0; args.arg_data_size = args.buffer_size - args.arg_data_offset; args.nevents = ring->nevents; args.str_storage = ring->str_storage; args.enforce_snaplen = false; /* * Fire the filler callback */ if (likely(g_ppm_events[event_type].filler_callback)) { cbres = g_ppm_events[event_type].filler_callback(&args); } else { pr_err("corrupted filler for event type %d: NULL callback\n", event_type); ASSERT(0); } if (likely(cbres == PPM_SUCCESS)) { /* * Validate that the filler added the right number of parameters */ if (likely(args.curarg == args.nargs)) { /* * The event was successfully inserted in the buffer */ event_size = sizeof(struct ppm_evt_hdr) + args.arg_data_offset; hdr->len = event_size; drop = 0; } else { pr_err("corrupted filler for event type %d (added %u args, should have added %u)\n", event_type, args.curarg, args.nargs); ASSERT(0); } } } if (likely(!drop)) { res = 1; next = head + event_size; if (unlikely(next >= RING_BUF_SIZE)) { /* * If something has been written in the cushion space at the end of * the buffer, copy it to the beginning and wrap the head around. * Note, we don't check that the copy fits because we assume that * filler_callback failed if the space was not enough. */ if (next > RING_BUF_SIZE) { memcpy(ring->buffer, ring->buffer + RING_BUF_SIZE, next - RING_BUF_SIZE); } next -= RING_BUF_SIZE; } /* * Make sure all the memory has been written in real memory before * we update the head and the user space process (on another CPU) * can access the buffer. */ smp_wmb(); ring_info->head = next; ++ring->nevents; } else { if (cbres == PPM_SUCCESS) { ASSERT(freespace < sizeof(struct ppm_evt_hdr) + args.arg_data_offset); ring_info->n_drops_buffer++; } else if (cbres == PPM_FAILURE_INVALID_USER_MEMORY) { #ifdef _DEBUG pr_err("Invalid read from user for event %d\n", event_type); #endif ring_info->n_drops_pf++; } else if (cbres == PPM_FAILURE_BUFFER_FULL) { ring_info->n_drops_buffer++; } else { ASSERT(false); } } if (ts->tv_sec > ring->last_print_time.tv_sec + 1) { vpr_info("consumer:%p CPU:%d, use:%d%%, ev:%llu, dr_buf:%llu, dr_pf:%llu, pr:%llu, cs:%llu\n", consumer->consumer_id, smp_processor_id(), (usedspace * 100) / RING_BUF_SIZE, ring_info->n_evts, ring_info->n_drops_buffer, ring_info->n_drops_pf, ring_info->n_preemptions, ring->info->n_context_switches); ring->last_print_time = *ts; } atomic_dec(&ring->preempt_count); put_cpu(); return res; } static inline void g_n_tracepoint_hit_inc(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) this_cpu_inc(g_n_tracepoint_hit); #elif defined(this_cpu_inc) /* this_cpu_inc has been added with 2.6.33 but backported by RHEL/CentOS to 2.6.32 * so just checking the existence of the symbol rather than matching the kernel version * https://github.com/torvalds/linux/commit/7340a0b15280c9d902c7dd0608b8e751b5a7c403 * * per_cpu_var removed with: * https://github.com/torvalds/linux/commit/dd17c8f72993f9461e9c19250e3f155d6d99df22 */ this_cpu_inc(per_cpu_var(g_n_tracepoint_hit)); #endif } TRACEPOINT_PROBE(syscall_enter_probe, struct pt_regs *regs, long id) { long table_index; const struct syscall_evt_pair *cur_g_syscall_table = g_syscall_table; const enum ppm_syscall_code *cur_g_syscall_code_routing_table = g_syscall_code_routing_table; bool compat = false; #ifdef __NR_socketcall int socketcall_syscall = __NR_socketcall; #else int socketcall_syscall = -1; #endif #if defined(CONFIG_X86_64) && defined(CONFIG_IA32_EMULATION) /* * If this is a 32bit process running on a 64bit kernel (see the CONFIG_IA32_EMULATION * kernel flag), we switch to the ia32 syscall table. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) if (in_ia32_syscall()) { #else if (unlikely(task_thread_info(current)->status & TS_COMPAT)) { #endif cur_g_syscall_table = g_syscall_ia32_table; cur_g_syscall_code_routing_table = g_syscall_ia32_code_routing_table; socketcall_syscall = __NR_ia32_socketcall; compat = true; } #endif g_n_tracepoint_hit_inc(); table_index = id - SYSCALL_TABLE_ID0; if (likely(table_index >= 0 && table_index < SYSCALL_TABLE_SIZE)) { struct event_data_t event_data; int used = cur_g_syscall_table[table_index].flags & UF_USED; enum syscall_flags drop_flags = cur_g_syscall_table[table_index].flags; enum ppm_event_type type; /* * Simple mode event filtering */ if (g_simple_mode_enabled) { if ((drop_flags & UF_SIMPLEDRIVER_KEEP) == 0) { return; } } #ifdef _HAS_SOCKETCALL if (id == socketcall_syscall) { used = true; drop_flags = UF_NEVER_DROP; type = PPME_GENERIC_E; } else type = cur_g_syscall_table[table_index].enter_event_type; #else type = cur_g_syscall_table[table_index].enter_event_type; #endif event_data.category = PPMC_SYSCALL; event_data.event_info.syscall_data.regs = regs; event_data.event_info.syscall_data.id = id; event_data.event_info.syscall_data.cur_g_syscall_code_routing_table = cur_g_syscall_code_routing_table; event_data.socketcall_syscall = socketcall_syscall; event_data.compat = compat; if (used) record_event_all_consumers(type, drop_flags, &event_data); else record_event_all_consumers(PPME_GENERIC_E, UF_ALWAYS_DROP, &event_data); } } TRACEPOINT_PROBE(syscall_exit_probe, struct pt_regs *regs, long ret) { int id; long table_index; const struct syscall_evt_pair *cur_g_syscall_table = g_syscall_table; const enum ppm_syscall_code *cur_g_syscall_code_routing_table = g_syscall_code_routing_table; bool compat = false; #ifdef __NR_socketcall int socketcall_syscall = __NR_socketcall; #else int socketcall_syscall = -1; #endif id = syscall_get_nr(current, regs); #if defined(CONFIG_X86_64) && defined(CONFIG_IA32_EMULATION) /* * When a process does execve from 64bit to 32bit, TS_COMPAT is marked true * but the id of the syscall is __NR_execve, so to correctly parse it we need to * use 64bit syscall table. On 32bit __NR_execve is equal to __NR_ia32_oldolduname * which is a very old syscall, not used anymore by most applications */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) if (in_ia32_syscall() && id != __NR_execve) { #else if (unlikely((task_thread_info(current)->status & TS_COMPAT) && id != __NR_execve)) { #endif cur_g_syscall_table = g_syscall_ia32_table; cur_g_syscall_code_routing_table = g_syscall_ia32_code_routing_table; socketcall_syscall = __NR_ia32_socketcall; compat = true; } #endif g_n_tracepoint_hit_inc(); table_index = id - SYSCALL_TABLE_ID0; if (likely(table_index >= 0 && table_index < SYSCALL_TABLE_SIZE)) { struct event_data_t event_data; int used = cur_g_syscall_table[table_index].flags & UF_USED; enum syscall_flags drop_flags = cur_g_syscall_table[table_index].flags; enum ppm_event_type type; /* * Simple mode event filtering */ if (g_simple_mode_enabled) { if ((drop_flags & UF_SIMPLEDRIVER_KEEP) == 0) { return; } } #ifdef _HAS_SOCKETCALL if (id == socketcall_syscall) { used = true; drop_flags = UF_NEVER_DROP; type = PPME_GENERIC_X; } else type = cur_g_syscall_table[table_index].exit_event_type; #else type = cur_g_syscall_table[table_index].exit_event_type; #endif event_data.category = PPMC_SYSCALL; event_data.event_info.syscall_data.regs = regs; event_data.event_info.syscall_data.id = id; event_data.event_info.syscall_data.cur_g_syscall_code_routing_table = cur_g_syscall_code_routing_table; event_data.socketcall_syscall = socketcall_syscall; event_data.compat = compat; if (used) record_event_all_consumers(type, drop_flags, &event_data); else record_event_all_consumers(PPME_GENERIC_X, UF_ALWAYS_DROP, &event_data); } } #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 1) int __access_remote_vm(struct task_struct *t, struct mm_struct *mm, unsigned long addr, void *buf, int len, int write); #endif TRACEPOINT_PROBE(syscall_procexit_probe, struct task_struct *p) { struct event_data_t event_data; g_n_tracepoint_hit_inc(); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) if (unlikely(current->flags & PF_KTHREAD)) { #else if (unlikely(current->flags & PF_BORROWED_MM)) { #endif /* * We are not interested in kernel threads */ return; } event_data.category = PPMC_CONTEXT_SWITCH; event_data.event_info.context_data.sched_prev = p; event_data.event_info.context_data.sched_next = p; record_event_all_consumers(PPME_PROCEXIT_1_E, UF_NEVER_DROP, &event_data); } #include #include #include #ifdef CAPTURE_CONTEXT_SWITCHES #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)) TRACEPOINT_PROBE(sched_switch_probe, struct rq *rq, struct task_struct *prev, struct task_struct *next) #elif (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)) TRACEPOINT_PROBE(sched_switch_probe, struct task_struct *prev, struct task_struct *next) #else TRACEPOINT_PROBE(sched_switch_probe, bool preempt, struct task_struct *prev, struct task_struct *next) #endif { struct event_data_t event_data; g_n_tracepoint_hit_inc(); event_data.category = PPMC_CONTEXT_SWITCH; event_data.event_info.context_data.sched_prev = prev; event_data.event_info.context_data.sched_next = next; record_event_all_consumers(PPME_SCHEDSWITCH_6_E, UF_USED, &event_data); } #endif #ifdef CAPTURE_SIGNAL_DELIVERIES TRACEPOINT_PROBE(signal_deliver_probe, int sig, struct siginfo *info, struct k_sigaction *ka) { struct event_data_t event_data; g_n_tracepoint_hit_inc(); event_data.category = PPMC_SIGNAL; event_data.event_info.signal_data.sig = sig; if (info == SEND_SIG_NOINFO || info == SEND_SIG_PRIV) event_data.event_info.signal_data.info = NULL; else event_data.event_info.signal_data.info = info; event_data.event_info.signal_data.ka = ka; record_event_all_consumers(PPME_SIGNALDELIVER_E, UF_USED | UF_ALWAYS_DROP, &event_data); } #endif #ifdef CAPTURE_PAGE_FAULTS TRACEPOINT_PROBE(page_fault_probe, unsigned long address, struct pt_regs *regs, unsigned long error_code) { struct event_data_t event_data; /* We register both tracepoints under the same probe and * sysdig event since there's little reason to expose this * complexity to the sysdig user. The distinction can still be made * in the output by looking for the USER_FAULT/SUPERVISOR_FAULT * flags */ g_n_tracepoint_hit_inc(); /* I still haven't decided if I'm interested in kernel threads or not. * For the moment, I assume yes since I can see some value for it. */ event_data.category = PPMC_PAGE_FAULT; event_data.event_info.fault_data.address = address; event_data.event_info.fault_data.regs = regs; event_data.event_info.fault_data.error_code = error_code; record_event_all_consumers(PPME_PAGE_FAULT_E, UF_ALWAYS_DROP, &event_data); } #endif static int init_ring_buffer(struct ppm_ring_buffer_context *ring) { unsigned int j; /* * Allocate the string storage in the ring descriptor */ ring->str_storage = (char *)__get_free_page(GFP_USER); if (!ring->str_storage) { pr_err("Error allocating the string storage\n"); goto init_ring_err; } /* * Allocate the buffer. * Note how we allocate 2 additional pages: they are used as additional overflow space for * the event data generation functions, so that they always operate on a contiguous buffer. */ ring->buffer = vmalloc(RING_BUF_SIZE + 2 * PAGE_SIZE); if (ring->buffer == NULL) { pr_err("Error allocating ring memory\n"); goto init_ring_err; } for (j = 0; j < RING_BUF_SIZE + 2 * PAGE_SIZE; j++) ring->buffer[j] = 0; /* * Allocate the buffer info structure */ ring->info = vmalloc(sizeof(struct ppm_ring_buffer_info)); if (ring->info == NULL) { pr_err("Error allocating ring memory\n"); goto init_ring_err; } /* * Initialize the buffer info structure */ reset_ring_buffer(ring); atomic_set(&ring->preempt_count, 0); pr_info("CPU buffer initialized, size=%d\n", RING_BUF_SIZE); return 1; init_ring_err: free_ring_buffer(ring); return 0; } static void free_ring_buffer(struct ppm_ring_buffer_context *ring) { if (ring->info) { vfree(ring->info); ring->info = NULL; } if (ring->buffer) { vfree((void *)ring->buffer); ring->buffer = NULL; } if (ring->str_storage) { free_page((unsigned long)ring->str_storage); ring->str_storage = NULL; } } static void reset_ring_buffer(struct ppm_ring_buffer_context *ring) { /* * ring->preempt_count is not reset to 0 on purpose, to prevent a race condition * see ppm_open */ ring->open = false; ring->capture_enabled = false; ring->info->head = 0; ring->info->tail = 0; ring->nevents = 0; ring->info->n_evts = 0; ring->info->n_drops_buffer = 0; ring->info->n_drops_pf = 0; ring->info->n_preemptions = 0; ring->info->n_context_switches = 0; getnstimeofday(&ring->last_print_time); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) static void visit_tracepoint(struct tracepoint *tp, void *priv) { if (!strcmp(tp->name, "sys_enter")) tp_sys_enter = tp; else if (!strcmp(tp->name, "sys_exit")) tp_sys_exit = tp; else if (!strcmp(tp->name, "sched_process_exit")) tp_sched_process_exit = tp; #ifdef CAPTURE_CONTEXT_SWITCHES else if (!strcmp(tp->name, "sched_switch")) tp_sched_switch = tp; #endif #ifdef CAPTURE_SIGNAL_DELIVERIES else if (!strcmp(tp->name, "signal_deliver")) tp_signal_deliver = tp; #endif #ifdef CAPTURE_PAGE_FAULTS else if (!strcmp(tp->name, "page_fault_user")) tp_page_fault_user = tp; else if (!strcmp(tp->name, "page_fault_kernel")) tp_page_fault_kernel = tp; #endif } static int get_tracepoint_handles(void) { for_each_kernel_tracepoint(visit_tracepoint, NULL); if (!tp_sys_enter) { pr_err("failed to find sys_enter tracepoint\n"); return -ENOENT; } if (!tp_sys_exit) { pr_err("failed to find sys_exit tracepoint\n"); return -ENOENT; } if (!tp_sched_process_exit) { pr_err("failed to find sched_process_exit tracepoint\n"); return -ENOENT; } #ifdef CAPTURE_CONTEXT_SWITCHES if (!tp_sched_switch) { pr_err("failed to find sched_switch tracepoint\n"); return -ENOENT; } #endif #ifdef CAPTURE_SIGNAL_DELIVERIES if (!tp_signal_deliver) { pr_err("failed to find signal_deliver tracepoint\n"); return -ENOENT; } #endif #ifdef CAPTURE_PAGE_FAULTS if (!tp_page_fault_user) { pr_notice("failed to find page_fault_user tracepoint, disabling page-faults\n"); g_fault_tracepoint_disabled = true; } if (!tp_page_fault_kernel) { pr_notice("failed to find page_fault_kernel tracepoint, disabling page-faults\n"); g_fault_tracepoint_disabled = true; } #endif return 0; } #else static int get_tracepoint_handles(void) { return 0; } #endif #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) static char *ppm_devnode(struct device *dev, umode_t *mode) #else static char *ppm_devnode(struct device *dev, mode_t *mode) #endif { if (mode) { *mode = 0400; if (dev) if (MINOR(dev->devt) == g_ppm_numdevs) *mode = 0222; } return NULL; } #endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) */ static int do_cpu_callback(unsigned long cpu, long sd_action) { struct ppm_ring_buffer_context *ring; struct ppm_consumer_t *consumer; struct event_data_t event_data; if (sd_action != 0) { rcu_read_lock(); list_for_each_entry_rcu(consumer, &g_consumer_list, node) { ring = per_cpu_ptr(consumer->ring_buffers, cpu); if (sd_action == 1) { /* * If the cpu was offline when the consumer was created, * this won't do anything because we never created a ring * buffer. We can't safely create one here because we're * in atomic context, and the consumer needs to call open * on this device anyways, so do it in ppm_open. */ ring->cpu_online = true; } else if (sd_action == 2) { ring->cpu_online = false; } } rcu_read_unlock(); event_data.category = PPMC_CONTEXT_SWITCH; event_data.event_info.context_data.sched_prev = (void *)cpu; event_data.event_info.context_data.sched_next = (void *)sd_action; record_event_all_consumers(PPME_CPU_HOTPLUG_E, UF_NEVER_DROP, &event_data); } return 0; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) static int sysdig_cpu_online(unsigned int cpu) { vpr_info("sysdig_cpu_online on cpu %d\n", cpu); return do_cpu_callback(cpu, 1); } static int sysdig_cpu_offline(unsigned int cpu) { vpr_info("sysdig_cpu_offline on cpu %d\n", cpu); return do_cpu_callback(cpu, 2); } #else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) */ /* * This gets called every time a CPU is added or removed */ static int cpu_callback(struct notifier_block *self, unsigned long action, void *hcpu) { unsigned long cpu = (unsigned long)hcpu; long sd_action = 0; switch (action) { case CPU_UP_PREPARE: #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) case CPU_UP_PREPARE_FROZEN: #endif sd_action = 1; break; case CPU_DOWN_PREPARE: #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) case CPU_DOWN_PREPARE_FROZEN: #endif sd_action = 2; break; default: break; } if (do_cpu_callback(cpu, sd_action) < 0) return NOTIFY_BAD; else return NOTIFY_OK; } static struct notifier_block cpu_notifier = { .notifier_call = &cpu_callback, .next = NULL, }; #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) */ int sysdig_init(void) { dev_t dev; unsigned int cpu; unsigned int num_cpus; int ret; int acrret = 0; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) int hp_ret; #endif int j; int n_created_devices = 0; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) struct device *device = NULL; #else struct class_device *device = NULL; #endif pr_info("driver loading, " PROBE_NAME " " PROBE_VERSION "\n"); ret = get_tracepoint_handles(); if (ret < 0) goto init_module_err; num_cpus = 0; for_each_possible_cpu(cpu) { ++num_cpus; } /* * Initialize the user I/O * ( + 1 for sysdig-events) */ acrret = alloc_chrdev_region(&dev, 0, num_cpus + 1, PROBE_DEVICE_NAME); if (acrret < 0) { pr_err("could not allocate major number for %s\n", PROBE_DEVICE_NAME); ret = -ENOMEM; goto init_module_err; } g_ppm_class = class_create(THIS_MODULE, PROBE_DEVICE_NAME); if (IS_ERR(g_ppm_class)) { pr_err("can't allocate device class\n"); ret = -EFAULT; goto init_module_err; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) g_ppm_class->devnode = ppm_devnode; #endif g_ppm_major = MAJOR(dev); g_ppm_numdevs = num_cpus; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) g_ppm_devs = kmalloc(g_ppm_numdevs * sizeof(struct ppm_device), GFP_KERNEL); #else g_ppm_devs = kmalloc_array(g_ppm_numdevs, sizeof(struct ppm_device), GFP_KERNEL); #endif if (!g_ppm_devs) { pr_err("can't allocate devices\n"); ret = -ENOMEM; goto init_module_err; } /* * We create a unique user level device for each of the ring buffers */ for (j = 0; j < g_ppm_numdevs; ++j) { cdev_init(&g_ppm_devs[j].cdev, &g_ppm_fops); g_ppm_devs[j].dev = MKDEV(g_ppm_major, j); if (cdev_add(&g_ppm_devs[j].cdev, g_ppm_devs[j].dev, 1) < 0) { pr_err("could not allocate chrdev for %s\n", PROBE_DEVICE_NAME); ret = -EFAULT; goto init_module_err; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) device = device_create( #else device = class_device_create( #endif g_ppm_class, NULL, /* no parent device */ g_ppm_devs[j].dev, NULL, /* no additional data */ PROBE_DEVICE_NAME "%d", j); if (IS_ERR(device)) { pr_err("error creating the device for %s\n", PROBE_DEVICE_NAME); cdev_del(&g_ppm_devs[j].cdev); ret = -EFAULT; goto init_module_err; } init_waitqueue_head(&g_ppm_devs[j].read_queue); n_created_devices++; } /* create_proc_read_entry(PPM_DEVICE_NAME, 0, NULL, ppm_read_proc, NULL); */ /* * Snaplen lookahead initialization */ if (dpi_lookahead_init() != PPM_SUCCESS) { pr_err("initializing lookahead-based snaplen failed\n"); ret = -EFAULT; goto init_module_err; } /* * Set up our callback in case we get a hotplug even while we are * initializing the cpu structures */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) hp_ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "sysdig/probe:online", sysdig_cpu_online, sysdig_cpu_offline); if (hp_ret <= 0) { pr_err("error registering cpu hotplug callback\n"); ret = hp_ret; goto init_module_err; } hp_state = hp_ret; #else register_cpu_notifier(&cpu_notifier); #endif /* * All ok. Final initializations. */ g_tracepoint_registered = false; return 0; init_module_err: for (j = 0; j < n_created_devices; ++j) { #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) device_destroy( #else class_device_destroy( #endif g_ppm_class, g_ppm_devs[j].dev); cdev_del(&g_ppm_devs[j].cdev); } if (g_ppm_class) class_destroy(g_ppm_class); if (acrret == 0) unregister_chrdev_region(dev, g_ppm_numdevs); kfree(g_ppm_devs); return ret; } void sysdig_exit(void) { int j; pr_info("driver unloading\n"); for (j = 0; j < g_ppm_numdevs; ++j) { #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) device_destroy( #else class_device_destroy( #endif g_ppm_class, g_ppm_devs[j].dev); cdev_del(&g_ppm_devs[j].cdev); } if (g_ppm_class) class_destroy(g_ppm_class); /* + 1 for sysdig-events */ unregister_chrdev_region(MKDEV(g_ppm_major, 0), g_ppm_numdevs + 1); kfree(g_ppm_devs); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) tracepoint_synchronize_unregister(); #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) if (hp_state > 0) cpuhp_remove_state_nocalls(hp_state); #else unregister_cpu_notifier(&cpu_notifier); #endif } module_init(sysdig_init); module_exit(sysdig_exit); module_param(max_consumers, uint, 0444); MODULE_PARM_DESC(max_consumers, "Maximum number of consumers that can simultaneously open the devices"); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) module_param(verbose, bool, 0444); #endif MODULE_PARM_DESC(verbose, "Enable verbose logging"); sysdig-0.26.4/driver/ppm.h000066400000000000000000000057741352731327100153660ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifndef PPM_H_ #define PPM_H_ #include /* * Our Own ASSERT implementation, so we can easily switch among BUG_ON, WARN_ON and nothing */ #ifdef _DEBUG #define ASSERT(expr) WARN_ON(!(expr)) #else #define ASSERT(expr) #endif #include /* * Global defines */ #define CAPTURE_CONTEXT_SWITCHES #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)) #define CAPTURE_SIGNAL_DELIVERIES #endif #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 12, 0)) && defined(CONFIG_X86) #define CAPTURE_PAGE_FAULTS #endif #define RW_SNAPLEN_EVENT 4096 #define DPI_LOOKAHEAD_SIZE 16 #define PPM_NULL_RDEV MKDEV(1, 3) #define PPM_PORT_MYSQL 3306 #define PPM_PORT_POSTGRES 5432 #define PPM_PORT_STATSD 8125 /* * The ring descriptor. * We have one of these for each CPU. */ struct ppm_ring_buffer_context { bool cpu_online; bool open; bool capture_enabled; struct ppm_ring_buffer_info *info; char *buffer; struct timespec last_print_time; u32 nevents; atomic_t preempt_count; char *str_storage; /* String storage. Size is one page. */ }; struct ppm_consumer_t { struct task_struct *consumer_id; #ifdef __percpu struct ppm_ring_buffer_context __percpu *ring_buffers; #else struct ppm_ring_buffer_context *ring_buffers; #endif u32 snaplen; u32 sampling_ratio; bool do_dynamic_snaplen; u32 sampling_interval; int is_dropping; int dropping_mode; volatile int need_to_insert_drop_e; volatile int need_to_insert_drop_x; struct list_head node; uint16_t fullcapture_port_range_start; uint16_t fullcapture_port_range_end; uint16_t statsd_port; }; #define STR_STORAGE_SIZE PAGE_SIZE /* * Global functions * * These are analogous to get_user(), copy_from_user() and strncpy_from_user(), * but they can't sleep, barf on page fault or be preempted */ #define ppm_get_user(x, ptr) ({ ppm_copy_from_user(&x, ptr, sizeof(x)) ? -EFAULT : 0; }) unsigned long ppm_copy_from_user(void *to, const void __user *from, unsigned long n); long ppm_strncpy_from_user(char *to, const char __user *from, unsigned long n); /* * Global tables */ #ifdef CONFIG_MIPS #define SYSCALL_TABLE_ID0 __NR_Linux #elif defined CONFIG_ARM #define SYSCALL_TABLE_ID0 __NR_SYSCALL_BASE #elif defined CONFIG_X86 || defined CONFIG_SUPERH #define SYSCALL_TABLE_ID0 0 #elif defined CONFIG_PPC64 #define SYSCALL_TABLE_ID0 0 #elif defined CONFIG_S390 #define SYSCALL_TABLE_ID0 0 #endif extern const struct syscall_evt_pair g_syscall_table[]; extern const struct ppm_event_info g_event_info[]; extern const enum ppm_syscall_code g_syscall_code_routing_table[]; #if defined(CONFIG_X86_64) && defined(CONFIG_IA32_EMULATION) extern const struct syscall_evt_pair g_syscall_ia32_table[]; extern const enum ppm_syscall_code g_syscall_ia32_code_routing_table[]; #endif extern void ppm_syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, unsigned long *args); #endif /* PPM_H_ */ sysdig-0.26.4/driver/ppm_compat_unistd_32.h000066400000000000000000000316061352731327100206140ustar00rootroot00000000000000#ifndef _ASM_X86_UNISTD_32_H #define _ASM_X86_UNISTD_32_H /* * This file contains the system call numbers. */ #define __NR_ia32_restart_syscall 0 #define __NR_ia32_exit 1 #define __NR_ia32_fork 2 #define __NR_ia32_read 3 #define __NR_ia32_write 4 #define __NR_ia32_open 5 #define __NR_ia32_close 6 #define __NR_ia32_waitpid 7 #define __NR_ia32_creat 8 #define __NR_ia32_link 9 #define __NR_ia32_unlink 10 #define __NR_ia32_execve 11 #define __NR_ia32_chdir 12 #define __NR_ia32_time 13 #define __NR_ia32_mknod 14 #define __NR_ia32_chmod 15 #define __NR_ia32_lchown 16 #define __NR_ia32_break 17 #define __NR_ia32_oldstat 18 #define __NR_ia32_lseek 19 #define __NR_ia32_getpid 20 #define __NR_ia32_mount 21 #define __NR_ia32_umount 22 #define __NR_ia32_setuid 23 #define __NR_ia32_getuid 24 #define __NR_ia32_stime 25 #define __NR_ia32_ptrace 26 #define __NR_ia32_alarm 27 #define __NR_ia32_oldfstat 28 #define __NR_ia32_pause 29 #define __NR_ia32_utime 30 #define __NR_ia32_stty 31 #define __NR_ia32_gtty 32 #define __NR_ia32_access 33 #define __NR_ia32_nice 34 #define __NR_ia32_ftime 35 #define __NR_ia32_sync 36 #define __NR_ia32_kill 37 #define __NR_ia32_rename 38 #define __NR_ia32_mkdir 39 #define __NR_ia32_rmdir 40 #define __NR_ia32_dup 41 #define __NR_ia32_pipe 42 #define __NR_ia32_times 43 #define __NR_ia32_prof 44 #define __NR_ia32_brk 45 #define __NR_ia32_setgid 46 #define __NR_ia32_getgid 47 #define __NR_ia32_signal 48 #define __NR_ia32_geteuid 49 #define __NR_ia32_getegid 50 #define __NR_ia32_acct 51 #define __NR_ia32_umount2 52 #define __NR_ia32_lock 53 #define __NR_ia32_ioctl 54 #define __NR_ia32_fcntl 55 #define __NR_ia32_mpx 56 #define __NR_ia32_setpgid 57 #define __NR_ia32_ulimit 58 #define __NR_ia32_oldolduname 59 #define __NR_ia32_umask 60 #define __NR_ia32_chroot 61 #define __NR_ia32_ustat 62 #define __NR_ia32_dup2 63 #define __NR_ia32_getppid 64 #define __NR_ia32_getpgrp 65 #define __NR_ia32_setsid 66 #define __NR_ia32_sigaction 67 #define __NR_ia32_sgetmask 68 #define __NR_ia32_ssetmask 69 #define __NR_ia32_setreuid 70 #define __NR_ia32_setregid 71 #define __NR_ia32_sigsuspend 72 #define __NR_ia32_sigpending 73 #define __NR_ia32_sethostname 74 #define __NR_ia32_setrlimit 75 #define __NR_ia32_getrlimit 76 /* Back compatible 2Gig limited rlimit */ #define __NR_ia32_getrusage 77 #define __NR_ia32_gettimeofday 78 #define __NR_ia32_settimeofday 79 #define __NR_ia32_getgroups 80 #define __NR_ia32_setgroups 81 #define __NR_ia32_select 82 #define __NR_ia32_symlink 83 #define __NR_ia32_oldlstat 84 #define __NR_ia32_readlink 85 #define __NR_ia32_uselib 86 #define __NR_ia32_swapon 87 #define __NR_ia32_reboot 88 #define __NR_ia32_readdir 89 #define __NR_ia32_mmap 90 #define __NR_ia32_munmap 91 #define __NR_ia32_truncate 92 #define __NR_ia32_ftruncate 93 #define __NR_ia32_fchmod 94 #define __NR_ia32_fchown 95 #define __NR_ia32_getpriority 96 #define __NR_ia32_setpriority 97 #define __NR_ia32_profil 98 #define __NR_ia32_statfs 99 #define __NR_ia32_fstatfs 100 #define __NR_ia32_ioperm 101 #define __NR_ia32_socketcall 102 #define __NR_ia32_syslog 103 #define __NR_ia32_setitimer 104 #define __NR_ia32_getitimer 105 #define __NR_ia32_stat 106 #define __NR_ia32_lstat 107 #define __NR_ia32_fstat 108 #define __NR_ia32_olduname 109 #define __NR_ia32_iopl 110 #define __NR_ia32_vhangup 111 #define __NR_ia32_idle 112 #define __NR_ia32_vm86old 113 #define __NR_ia32_wait4 114 #define __NR_ia32_swapoff 115 #define __NR_ia32_sysinfo 116 #define __NR_ia32_ipc 117 #define __NR_ia32_fsync 118 #define __NR_ia32_sigreturn 119 #define __NR_ia32_clone 120 #define __NR_ia32_setdomainname 121 #define __NR_ia32_uname 122 #define __NR_ia32_modify_ldt 123 #define __NR_ia32_adjtimex 124 #define __NR_ia32_mprotect 125 #define __NR_ia32_sigprocmask 126 #define __NR_ia32_create_module 127 #define __NR_ia32_init_module 128 #define __NR_ia32_delete_module 129 #define __NR_ia32_get_kernel_syms 130 #define __NR_ia32_quotactl 131 #define __NR_ia32_getpgid 132 #define __NR_ia32_fchdir 133 #define __NR_ia32_bdflush 134 #define __NR_ia32_sysfs 135 #define __NR_ia32_personality 136 #define __NR_ia32_afs_syscall 137 /* Syscall for Andrew File System */ #define __NR_ia32_setfsuid 138 #define __NR_ia32_setfsgid 139 #define __NR_ia32__llseek 140 #define __NR_ia32_getdents 141 #define __NR_ia32__newselect 142 #define __NR_ia32_flock 143 #define __NR_ia32_msync 144 #define __NR_ia32_readv 145 #define __NR_ia32_writev 146 #define __NR_ia32_getsid 147 #define __NR_ia32_fdatasync 148 #define __NR_ia32__sysctl 149 #define __NR_ia32_mlock 150 #define __NR_ia32_munlock 151 #define __NR_ia32_mlockall 152 #define __NR_ia32_munlockall 153 #define __NR_ia32_sched_setparam 154 #define __NR_ia32_sched_getparam 155 #define __NR_ia32_sched_setscheduler 156 #define __NR_ia32_sched_getscheduler 157 #define __NR_ia32_sched_yield 158 #define __NR_ia32_sched_get_priority_max 159 #define __NR_ia32_sched_get_priority_min 160 #define __NR_ia32_sched_rr_get_interval 161 #define __NR_ia32_nanosleep 162 #define __NR_ia32_mremap 163 #define __NR_ia32_setresuid 164 #define __NR_ia32_getresuid 165 #define __NR_ia32_vm86 166 #define __NR_ia32_query_module 167 #define __NR_ia32_poll 168 #define __NR_ia32_nfsservctl 169 #define __NR_ia32_setresgid 170 #define __NR_ia32_getresgid 171 #define __NR_ia32_prctl 172 #define __NR_ia32_rt_sigreturn 173 #define __NR_ia32_rt_sigaction 174 #define __NR_ia32_rt_sigprocmask 175 #define __NR_ia32_rt_sigpending 176 #define __NR_ia32_rt_sigtimedwait 177 #define __NR_ia32_rt_sigqueueinfo 178 #define __NR_ia32_rt_sigsuspend 179 #define __NR_ia32_pread64 180 #define __NR_ia32_pwrite64 181 #define __NR_ia32_chown 182 #define __NR_ia32_getcwd 183 #define __NR_ia32_capget 184 #define __NR_ia32_capset 185 #define __NR_ia32_sigaltstack 186 #define __NR_ia32_sendfile 187 #define __NR_ia32_getpmsg 188 /* some people actually want streams */ #define __NR_ia32_putpmsg 189 /* some people actually want streams */ #define __NR_ia32_vfork 190 #define __NR_ia32_ugetrlimit 191 /* SuS compliant getrlimit */ #define __NR_ia32_mmap2 192 #define __NR_ia32_truncate64 193 #define __NR_ia32_ftruncate64 194 #define __NR_ia32_stat64 195 #define __NR_ia32_lstat64 196 #define __NR_ia32_fstat64 197 #define __NR_ia32_lchown32 198 #define __NR_ia32_getuid32 199 #define __NR_ia32_getgid32 200 #define __NR_ia32_geteuid32 201 #define __NR_ia32_getegid32 202 #define __NR_ia32_setreuid32 203 #define __NR_ia32_setregid32 204 #define __NR_ia32_getgroups32 205 #define __NR_ia32_setgroups32 206 #define __NR_ia32_fchown32 207 #define __NR_ia32_setresuid32 208 #define __NR_ia32_getresuid32 209 #define __NR_ia32_setresgid32 210 #define __NR_ia32_getresgid32 211 #define __NR_ia32_chown32 212 #define __NR_ia32_setuid32 213 #define __NR_ia32_setgid32 214 #define __NR_ia32_setfsuid32 215 #define __NR_ia32_setfsgid32 216 #define __NR_ia32_pivot_root 217 #define __NR_ia32_mincore 218 #define __NR_ia32_madvise 219 #define __NR_ia32_madvise1 219 /* delete when C lib stub is removed */ #define __NR_ia32_getdents64 220 #define __NR_ia32_fcntl64 221 /* 223 is unused */ #define __NR_ia32_gettid 224 #define __NR_ia32_readahead 225 #define __NR_ia32_setxattr 226 #define __NR_ia32_lsetxattr 227 #define __NR_ia32_fsetxattr 228 #define __NR_ia32_getxattr 229 #define __NR_ia32_lgetxattr 230 #define __NR_ia32_fgetxattr 231 #define __NR_ia32_listxattr 232 #define __NR_ia32_llistxattr 233 #define __NR_ia32_flistxattr 234 #define __NR_ia32_removexattr 235 #define __NR_ia32_lremovexattr 236 #define __NR_ia32_fremovexattr 237 #define __NR_ia32_tkill 238 #define __NR_ia32_sendfile64 239 #define __NR_ia32_futex 240 #define __NR_ia32_sched_setaffinity 241 #define __NR_ia32_sched_getaffinity 242 #define __NR_ia32_set_thread_area 243 #define __NR_ia32_get_thread_area 244 #define __NR_ia32_io_setup 245 #define __NR_ia32_io_destroy 246 #define __NR_ia32_io_getevents 247 #define __NR_ia32_io_submit 248 #define __NR_ia32_io_cancel 249 #define __NR_ia32_fadvise64 250 /* 251 is available for reuse (was briefly sys_set_zone_reclaim) */ #define __NR_ia32_exit_group 252 #define __NR_ia32_lookup_dcookie 253 #define __NR_ia32_epoll_create 254 #define __NR_ia32_epoll_ctl 255 #define __NR_ia32_epoll_wait 256 #define __NR_ia32_remap_file_pages 257 #define __NR_ia32_set_tid_address 258 #define __NR_ia32_timer_create 259 #define __NR_ia32_timer_settime (__NR_timer_create+1) #define __NR_ia32_timer_gettime (__NR_timer_create+2) #define __NR_ia32_timer_getoverrun (__NR_timer_create+3) #define __NR_ia32_timer_delete (__NR_timer_create+4) #define __NR_ia32_clock_settime (__NR_timer_create+5) #define __NR_ia32_clock_gettime (__NR_timer_create+6) #define __NR_ia32_clock_getres (__NR_timer_create+7) #define __NR_ia32_clock_nanosleep (__NR_timer_create+8) #define __NR_ia32_statfs64 268 #define __NR_ia32_fstatfs64 269 #define __NR_ia32_tgkill 270 #define __NR_ia32_utimes 271 #define __NR_ia32_fadvise64_64 272 #define __NR_ia32_vserver 273 #define __NR_ia32_mbind 274 #define __NR_ia32_get_mempolicy 275 #define __NR_ia32_set_mempolicy 276 #define __NR_ia32_mq_open 277 #define __NR_ia32_mq_unlink (__NR_mq_open+1) #define __NR_ia32_mq_timedsend (__NR_mq_open+2) #define __NR_ia32_mq_timedreceive (__NR_mq_open+3) #define __NR_ia32_mq_notify (__NR_mq_open+4) #define __NR_ia32_mq_getsetattr (__NR_mq_open+5) #define __NR_ia32_kexec_load 283 #define __NR_ia32_waitid 284 /* #define __NR_ia32_sys_setaltroot 285 */ #define __NR_ia32_add_key 286 #define __NR_ia32_request_key 287 #define __NR_ia32_keyctl 288 #define __NR_ia32_ioprio_set 289 #define __NR_ia32_ioprio_get 290 #define __NR_ia32_inotify_init 291 #define __NR_ia32_inotify_add_watch 292 #define __NR_ia32_inotify_rm_watch 293 #define __NR_ia32_migrate_pages 294 #define __NR_ia32_openat 295 #define __NR_ia32_mkdirat 296 #define __NR_ia32_mknodat 297 #define __NR_ia32_fchownat 298 #define __NR_ia32_futimesat 299 #define __NR_ia32_fstatat64 300 #define __NR_ia32_unlinkat 301 #define __NR_ia32_renameat 302 #define __NR_ia32_linkat 303 #define __NR_ia32_symlinkat 304 #define __NR_ia32_readlinkat 305 #define __NR_ia32_fchmodat 306 #define __NR_ia32_faccessat 307 #define __NR_ia32_pselect6 308 #define __NR_ia32_ppoll 309 #define __NR_ia32_unshare 310 #define __NR_ia32_set_robust_list 311 #define __NR_ia32_get_robust_list 312 #define __NR_ia32_splice 313 #define __NR_ia32_sync_file_range 314 #define __NR_ia32_tee 315 #define __NR_ia32_vmsplice 316 #define __NR_ia32_move_pages 317 #define __NR_ia32_getcpu 318 #define __NR_ia32_epoll_pwait 319 #define __NR_ia32_utimensat 320 #define __NR_ia32_signalfd 321 #define __NR_ia32_timerfd_create 322 #define __NR_ia32_eventfd 323 #define __NR_ia32_fallocate 324 #define __NR_ia32_timerfd_settime 325 #define __NR_ia32_timerfd_gettime 326 #define __NR_ia32_signalfd4 327 #define __NR_ia32_eventfd2 328 #define __NR_ia32_epoll_create1 329 #define __NR_ia32_dup3 330 #define __NR_ia32_pipe2 331 #define __NR_ia32_inotify_init1 332 #define __NR_ia32_preadv 333 #define __NR_ia32_pwritev 334 #define __NR_ia32_rt_tgsigqueueinfo 335 #define __NR_ia32_perf_event_open 336 #define __NR_ia32_recvmmsg 337 #define __NR_ia32_fanotify_init 338 #define __NR_ia32_fanotify_mark 339 #define __NR_ia32_prlimit64 340 #define __NR_ia32_name_to_handle_at 341 #define __NR_ia32_open_by_handle_at 342 #define __NR_ia32_clock_adjtime 343 #define __NR_ia32_syncfs 344 #define __NR_ia32_sendmmsg 345 #define __NR_ia32_setns 346 #define __NR_ia32_process_vm_readv 347 #define __NR_ia32_process_vm_writev 348 #ifdef __KERNEL__ #define NR_ia32_syscalls 349 #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_ALARM #define __ARCH_WANT_SYS_GETHOSTNAME #define __ARCH_WANT_SYS_IPC #define __ARCH_WANT_SYS_PAUSE #define __ARCH_WANT_SYS_SGETMASK #define __ARCH_WANT_SYS_SIGNAL #define __ARCH_WANT_SYS_TIME #define __ARCH_WANT_SYS_UTIME #define __ARCH_WANT_SYS_WAITPID #define __ARCH_WANT_SYS_SOCKETCALL #define __ARCH_WANT_SYS_FADVISE64 #define __ARCH_WANT_SYS_GETPGRP #define __ARCH_WANT_SYS_LLSEEK #define __ARCH_WANT_SYS_NICE #define __ARCH_WANT_SYS_OLD_GETRLIMIT #define __ARCH_WANT_SYS_OLD_UNAME #define __ARCH_WANT_SYS_OLD_MMAP #define __ARCH_WANT_SYS_OLD_SELECT #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGSUSPEND /* * "Conditional" syscalls * * What we want is __attribute__((weak,alias("sys_ni_syscall"))), * but it doesn't work on all toolchains, so we just do it by hand */ #ifndef cond_syscall #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") #endif #endif /* __KERNEL__ */ #endif /* _ASM_X86_UNISTD_32_H */ sysdig-0.26.4/driver/ppm_cputime.c000066400000000000000000000225371352731327100171030ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #include // These function are taken from the linux kernel and are used only // on versions that don't export task_cputime_adjusted() #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)) #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)) #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "ppm_ringbuffer.h" #include "ppm_events_public.h" #include "ppm_events.h" #include "ppm.h" #include "ppm_version.h" #if (defined CONFIG_VIRT_CPU_ACCOUNTING_NATIVE) || (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)) void ppm_task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st) { *ut = p->utime; *st = p->stime; } #else #ifndef cmpxchg_cputime #define cmpxchg_cputime(ptr, old, new) cmpxchg(ptr, old, new) #endif #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) || (PPM_RHEL_RELEASE_CODE > 0 && PPM_RHEL_RELEASE_CODE >= PPM_RHEL_RELEASE_VERSION(7, 7)) #define ppm_vtime_starttime(tsk) ((tsk)->vtime.starttime) #define ppm_vtime_seqlock(tsk) (&(tsk)->vtime.seqlock) #define ppm_vtime_state(tsk) ((tsk)->vtime.state) #else #define ppm_vtime_starttime(tsk) ((tsk)->vtime_snap) #define ppm_vtime_seqlock(tsk) (&(tsk)->vtime_seqlock) #define ppm_vtime_state(tsk) ((tsk)->vtime_snap_whence) #endif static unsigned long long vtime_delta(struct task_struct *tsk) { unsigned long long clock; clock = local_clock(); if (clock < ppm_vtime_starttime(tsk)) return 0; return clock - ppm_vtime_starttime(tsk); } static void fetch_task_cputime(struct task_struct *t, cputime_t *u_dst, cputime_t *s_dst, cputime_t *u_src, cputime_t *s_src, cputime_t *udelta, cputime_t *sdelta) { unsigned int seq; unsigned long long delta; do { *udelta = 0; *sdelta = 0; seq = read_seqbegin(ppm_vtime_seqlock(t)); if (u_dst) *u_dst = *u_src; if (s_dst) *s_dst = *s_src; /* Task is sleeping, nothing to add */ if (ppm_vtime_state(t) == VTIME_SLEEPING || is_idle_task(t)) continue; delta = vtime_delta(t); /* * Task runs either in user or kernel space, add pending nohz time to * the right place. */ if (ppm_vtime_state(t) == VTIME_USER || t->flags & PF_VCPU) { *udelta = delta; } else { if (ppm_vtime_state(t) == VTIME_SYS) *sdelta = delta; } } while (read_seqretry(ppm_vtime_seqlock(t), seq)); } void task_cputime(struct task_struct *t, cputime_t *utime, cputime_t *stime) { cputime_t udelta, sdelta; fetch_task_cputime(t, utime, stime, &t->utime, &t->stime, &udelta, &sdelta); if (utime) *utime += udelta; if (stime) *stime += sdelta; } #elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0) static inline void task_cputime(struct task_struct *t, cputime_t *utime, cputime_t *stime) { if (utime) *utime = t->utime; if (stime) *stime = t->stime; } #endif /* CONFIG_VIRT_CPU_ACCOUNTING_GEN */ u64 nsecs_to_jiffies64(u64 n) { #if (NSEC_PER_SEC % HZ) == 0 /* Common case, HZ = 100, 128, 200, 250, 256, 500, 512, 1000 etc. */ return div_u64(n, NSEC_PER_SEC / HZ); #elif (HZ % 512) == 0 /* overflow after 292 years if HZ = 1024 */ return div_u64(n * HZ / 512, NSEC_PER_SEC / 512); #else /* * Generic case - optimized for cases where HZ is a multiple of 3. * overflow after 64.99 years, exact for HZ = 60, 72, 90, 120 etc. */ return div_u64(n * 9, (9ull * NSEC_PER_SEC + HZ / 2) / HZ); #endif } unsigned long nsecs_to_jiffies(u64 n) { return (unsigned long)nsecs_to_jiffies64(n); } #ifndef nsecs_to_cputime #ifdef msecs_to_cputime #define nsecs_to_cputime(__nsecs) \ msecs_to_cputime(div_u64((__nsecs), NSEC_PER_MSEC)) #else #define nsecs_to_cputime(__nsecs) nsecs_to_jiffies(__nsecs) #endif #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)) /* * Perform (stime * rtime) / total, but avoid multiplication overflow by * loosing precision when the numbers are big. */ static cputime_t scale_stime(u64 stime, u64 rtime, u64 total) { u64 scaled; for (;;) { /* Make sure "rtime" is the bigger of stime/rtime */ if (stime > rtime) swap(rtime, stime); /* Make sure 'total' fits in 32 bits */ if (total >> 32) goto drop_precision; /* Does rtime (and thus stime) fit in 32 bits? */ if (!(rtime >> 32)) break; /* Can we just balance rtime/stime rather than dropping bits? */ if (stime >> 31) goto drop_precision; /* We can grow stime and shrink rtime and try to make them both fit */ stime <<= 1; rtime >>= 1; continue; drop_precision: /* We drop from rtime, it has more bits than stime */ rtime >>= 1; total >>= 1; } /* * Make sure gcc understands that this is a 32x32->64 multiply, * followed by a 64/32->64 divide. */ scaled = div_u64((u64) (u32) stime * (u64) (u32) rtime, (u32)total); return (__force cputime_t) scaled; } /* * Atomically advance counter to the new value. Interrupts, vcpu * scheduling, and scaling inaccuracies can cause cputime_advance * to be occasionally called with a new value smaller than counter. * Let's enforce atomicity. * * Normally a caller will only go through this loop once, or not * at all in case a previous caller updated counter the same jiffy. */ static void cputime_advance(cputime_t *counter, cputime_t new) { cputime_t old; while (new > (old = ACCESS_ONCE(*counter))) cmpxchg_cputime(counter, old, new); } /* * Adjust tick based cputime random precision against scheduler * runtime accounting. */ static void cputime_adjust(struct task_cputime *curr, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)) struct prev_cputime *prev, #else struct cputime *prev, #endif cputime_t *ut, cputime_t *st) { cputime_t rtime, stime, utime; /* * Tick based cputime accounting depend on random scheduling * timeslices of a task to be interrupted or not by the timer. * Depending on these circumstances, the number of these interrupts * may be over or under-optimistic, matching the real user and system * cputime with a variable precision. * * Fix this by scaling these tick based values against the total * runtime accounted by the CFS scheduler. */ rtime = nsecs_to_cputime(curr->sum_exec_runtime); /* * Update userspace visible utime/stime values only if actual execution * time is bigger than already exported. Note that can happen, that we * provided bigger values due to scaling inaccuracy on big numbers. */ if (prev->stime + prev->utime >= rtime) goto out; stime = curr->stime; utime = curr->utime; if (utime == 0) { stime = rtime; } else if (stime == 0) { utime = rtime; } else { cputime_t total = stime + utime; stime = scale_stime((__force u64)stime, (__force u64)rtime, (__force u64)total); utime = rtime - stime; } cputime_advance(&prev->stime, stime); cputime_advance(&prev->utime, utime); out: *ut = prev->utime; *st = prev->stime; } void ppm_task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st) { struct task_cputime cputime = { #ifdef CONFIG_SCHED_BFS .sum_exec_runtime = tsk_seruntime(p), #else .sum_exec_runtime = p->se.sum_exec_runtime, #endif }; task_cputime(p, &cputime.utime, &cputime.stime); cputime_adjust(&cputime, &p->prev_cputime, ut, st); } #else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) */ static cputime_t scale_utime(cputime_t utime, cputime_t rtime, cputime_t total) { u64 temp = (__force u64) rtime; temp *= (__force u64) utime; if (sizeof(cputime_t) == 4) temp = div_u64(temp, (__force u32) total); else temp = div64_u64(temp, (__force u64) total); return (__force cputime_t) temp; } // Taken from task_times(struct task_struct *p, cputime_t *ut, cputime_t *st) void ppm_task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st) { cputime_t rtime, utime = p->utime, total = utime + p->stime; /* * Use CFS's precise accounting: */ rtime = nsecs_to_cputime(p->se.sum_exec_runtime); if (total) utime = scale_utime(utime, rtime, total); else utime = rtime; /* * Compare with previous values, to keep monotonicity: */ p->prev_utime = max(p->prev_utime, utime); p->prev_stime = max(p->prev_stime, rtime - p->prev_utime); *ut = p->prev_utime; *st = p->prev_stime; } #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)) */ #endif /* (defined CONFIG_VIRT_CPU_ACCOUNTING_NATIVE) || (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)) */ #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)) */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)) #include #include /* * Implementation copied from kernel/time/time.c in 4.11.0 */ u64 nsec_to_clock_t(u64 x) { #if (NSEC_PER_SEC % USER_HZ) == 0 return div_u64(x, NSEC_PER_SEC / USER_HZ); #elif (USER_HZ % 512) == 0 return div_u64(x * USER_HZ / 512, NSEC_PER_SEC / 512); #else /* * max relative error 5.7e-8 (1.8s per year) for USER_HZ <= 1024, * overflow after 64.99 years * exact for HZ=60, 72, 90, 120, 144, 180, 300, 600, 900, ... */ return div_u64(x * 9, (9ull * NSEC_PER_SEC + (USER_HZ / 2)) / USER_HZ); #endif } #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) */ sysdig-0.26.4/driver/ppm_events.c000066400000000000000000001074621352731327100167420ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 20) #include #include "ppm_syscall.h" #else #include #endif #include "ppm_ringbuffer.h" #include "ppm_events_public.h" #include "ppm_events.h" #include "ppm.h" #include "ppm_flag_helpers.h" #include "ppm_version.h" /* * The kernel patched with grsecurity makes the default access_ok trigger a * might_sleep(), so if present we use the one defined by them */ #ifdef access_ok_noprefault #define ppm_access_ok access_ok_noprefault #else #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) || (PPM_RHEL_RELEASE_CODE > 0 && PPM_RHEL_RELEASE_CODE >= PPM_RHEL_RELEASE_VERSION(8, 1)) #define ppm_access_ok(type, addr, size) access_ok(addr, size) #else #define ppm_access_ok(type, addr, size) access_ok(type, addr, size) #endif #endif extern bool g_tracers_enabled; static void memory_dump(char *p, size_t size) { unsigned int j; for (j = 0; j < size; j += 8) pr_info("%*ph\n", 8, &p[j]); } static inline bool in_port_range(uint16_t port, uint16_t min, uint16_t max) { return port >= min && port <= max; } /* * Globals */ u32 g_http_options_intval; u32 g_http_get_intval; u32 g_http_head_intval; u32 g_http_post_intval; u32 g_http_put_intval; u32 g_http_delete_intval; u32 g_http_trace_intval; u32 g_http_connect_intval; u32 g_http_resp_intval; /* * What this function does is basically a special memcpy * so that, if the page fault handler detects the address is invalid, * won't kill the process but will return a positive number * Plus, this doesn't sleep. * The risk is that if the buffer is partially paged out, we get an error. * Returns the number of bytes NOT read. */ unsigned long ppm_copy_from_user(void *to, const void __user *from, unsigned long n) { unsigned long res = n; pagefault_disable(); if (likely(ppm_access_ok(VERIFY_READ, from, n))) res = __copy_from_user_inatomic(to, from, n); pagefault_enable(); return res; } /* * On some kernels (e.g. 2.6.39), even with preemption disabled, the strncpy_from_user, * instead of returning -1 after a page fault, schedules the process, so we drop events * because of the preemption. This function reads the user buffer in atomic chunks, and * returns when there's an error or the terminator is found */ long ppm_strncpy_from_user(char *to, const char __user *from, unsigned long n) { long string_length = 0; long res = -1; unsigned long bytes_to_read = 4; int j; pagefault_disable(); while (n) { /* * Read bytes_to_read bytes at a time, and look for the terminator. Should be fast * since the copy_from_user is optimized for the processor */ if (n < bytes_to_read) bytes_to_read = n; if (!ppm_access_ok(VERIFY_READ, from, bytes_to_read)) { res = -1; goto strncpy_end; } if (__copy_from_user_inatomic(to, from, bytes_to_read)) { /* * Page fault */ res = -1; goto strncpy_end; } n -= bytes_to_read; from += bytes_to_read; for (j = 0; j < bytes_to_read; ++j) { ++string_length; if (!*to) { res = string_length; goto strncpy_end; } ++to; } } strncpy_end: pagefault_enable(); return res; } int32_t dpi_lookahead_init(void) { g_http_options_intval = (*(u32 *)HTTP_OPTIONS_STR); g_http_get_intval = (*(u32 *)HTTP_GET_STR); g_http_head_intval = (*(u32 *)HTTP_HEAD_STR); g_http_post_intval = (*(u32 *)HTTP_POST_STR); g_http_put_intval = (*(u32 *)HTTP_PUT_STR); g_http_delete_intval = (*(u32 *)HTTP_DELETE_STR); g_http_trace_intval = (*(u32 *)HTTP_TRACE_STR); g_http_connect_intval = (*(u32 *)HTTP_CONNECT_STR); g_http_resp_intval = (*(u32 *)HTTP_RESP_STR); return PPM_SUCCESS; } inline int sock_getname(struct socket* sock, struct sockaddr* sock_address, int peer) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) int ret = sock->ops->getname(sock, sock_address, peer); if (ret >= 0) ret = 0; return ret; #else int sockaddr_len; return sock->ops->getname(sock, sock_address, &sockaddr_len, peer); #endif } inline u32 compute_snaplen(struct event_filler_arguments *args, char *buf, u32 lookahead_size) { u32 res = args->consumer->snaplen; int err; struct socket *sock; sa_family_t family; struct sockaddr_storage sock_address; struct sockaddr_storage peer_address; u16 sport, dport; if (g_tracers_enabled && args->event_type == PPME_SYSCALL_WRITE_X) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) struct fd f = fdget(args->fd); if (f.file && f.file->f_inode) { if (f.file->f_inode->i_rdev == PPM_NULL_RDEV) { res = RW_SNAPLEN_EVENT; fdput(f); return res; } fdput(f); } #else struct file* file = fget(args->fd); /* Use cached f_inode only on kernel versions that have it * https://github.com/torvalds/linux/commit/dd37978c50bc8b354e5c4633f69387f16572fdac */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) if (file && file->f_inode) { if (file->f_inode->i_rdev == PPM_NULL_RDEV) { // Use f_dentry for older kernel versions #elif LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,20) if (file && file->f_dentry && file->f_dentry->d_inode) { if (file->f_dentry->d_inode->i_rdev == PPM_NULL_RDEV) { #else if (file && file->f_path.dentry && file->f_path.dentry->d_inode) { if (file->f_path.dentry->d_inode->i_rdev == PPM_NULL_RDEV) { #endif res = RW_SNAPLEN_EVENT; fput(file); return res; } fput(file); } #endif } if (!args->consumer->do_dynamic_snaplen) return res; sock = sockfd_lookup(args->fd, &err); if (sock) { if (sock->sk) { err = sock_getname(sock, (struct sockaddr *)&sock_address, 0); if (err == 0) { if(args->event_type == PPME_SOCKET_SENDTO_X) { unsigned long syscall_args[6] = {}; unsigned long val; struct sockaddr __user * usrsockaddr; /* * Get the address */ if (!args->is_socketcall) { ppm_syscall_get_arguments(current, args->regs, syscall_args); val = syscall_args[4]; } else val = args->socketcall_args[4]; usrsockaddr = (struct sockaddr __user *)val; if(usrsockaddr == NULL) { /* * Suppose is a connected socket, fall back to fd */ err = sock_getname(sock, (struct sockaddr *)&peer_address, 1); } else { /* * Get the address len */ if (!args->is_socketcall) { ppm_syscall_get_arguments(current, args->regs, syscall_args); val = syscall_args[5]; } else val = args->socketcall_args[5]; if (val != 0) { /* * Copy the address */ err = addr_to_kernel(usrsockaddr, val, (struct sockaddr *)&peer_address); } else { /* * This case should be very rare, fallback again to sock */ err = sock_getname(sock, (struct sockaddr *)&peer_address, 1); } } } else if (args->event_type == PPME_SOCKET_SENDMSG_X) { unsigned long syscall_args[6] = {}; unsigned long val; struct sockaddr __user * usrsockaddr; int addrlen; #ifdef CONFIG_COMPAT struct compat_msghdr compat_mh; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) struct user_msghdr mh; #else struct msghdr mh; #endif if (!args->is_socketcall) { ppm_syscall_get_arguments(current, args->regs, syscall_args); val = syscall_args[1]; } else val = args->socketcall_args[1]; #ifdef CONFIG_COMPAT if (!args->compat) { #endif if (unlikely(ppm_copy_from_user(&mh, (const void __user *)val, sizeof(mh)))) { usrsockaddr = NULL; addrlen = 0; } else { usrsockaddr = (struct sockaddr __user *)mh.msg_name; addrlen = mh.msg_namelen; } #ifdef CONFIG_COMPAT } else { if (unlikely(ppm_copy_from_user(&compat_mh, (const void __user *)compat_ptr(val), sizeof(compat_mh)))) { usrsockaddr = NULL; addrlen = 0; } else { usrsockaddr = (struct sockaddr __user *)compat_ptr(compat_mh.msg_name); addrlen = compat_mh.msg_namelen; } } #endif if (usrsockaddr != NULL && addrlen != 0) { /* * Copy the address */ err = addr_to_kernel(usrsockaddr, addrlen, (struct sockaddr *)&peer_address); } else /* * Suppose it is a connected socket, fall back to fd */ err = sock_getname(sock, (struct sockaddr *)&peer_address, 1); } else err = sock_getname(sock, (struct sockaddr *)&peer_address, 1); if (err == 0) { uint16_t min_port = args->consumer->fullcapture_port_range_start; uint16_t max_port = args->consumer->fullcapture_port_range_end; family = sock->sk->sk_family; if (family == AF_INET) { sport = ntohs(((struct sockaddr_in *) &sock_address)->sin_port); dport = ntohs(((struct sockaddr_in *) &peer_address)->sin_port); } else if (family == AF_INET6) { sport = ntohs(((struct sockaddr_in6 *) &sock_address)->sin6_port); dport = ntohs(((struct sockaddr_in6 *) &peer_address)->sin6_port); } else { sport = 0; dport = 0; } if (max_port > 0 && (in_port_range(sport, min_port, max_port) || in_port_range(dport, min_port, max_port))) { /* * Before checking the well-known ports, see if the user has requested * an increased snaplen for the port in question. */ sockfd_put(sock); return RW_MAX_FULLCAPTURE_PORT_SNAPLEN; } else if (sport == PPM_PORT_MYSQL || dport == PPM_PORT_MYSQL) { if (lookahead_size >= 5) { if (buf[0] == 3 || buf[1] == 3 || buf[2] == 3 || buf[3] == 3 || buf[4] == 3) { sockfd_put(sock); return 2000; } else if (buf[2] == 0 && buf[3] == 0) { sockfd_put(sock); return 2000; } } } else if (sport == PPM_PORT_POSTGRES || dport == PPM_PORT_POSTGRES) { if (lookahead_size >= 2) { if ((buf[0] == 'Q' && buf[1] == 0) || /* SimpleQuery command */ (buf[0] == 'P' && buf[1] == 0) || /* Prepare statement command */ (buf[4] == 0 && buf[5] == 3 && buf[6] == 0) || /* startup command */ (buf[0] == 'E' && buf[1] == 0) /* error or execute command */ ) { sockfd_put(sock); return 2000; } } } else if ((lookahead_size >= 4 && buf[1] == 0 && buf[2] == 0 && buf[2] == 0) || /* matches command */ (lookahead_size >= 16 && (*(int32_t *)(buf+12) == 1 || /* matches header */ *(int32_t *)(buf+12) == 2001 || *(int32_t *)(buf+12) == 2002 || *(int32_t *)(buf+12) == 2003 || *(int32_t *)(buf+12) == 2004 || *(int32_t *)(buf+12) == 2005 || *(int32_t *)(buf+12) == 2006 || *(int32_t *)(buf+12) == 2007) ) ) { sockfd_put(sock); return 2000; } else if (dport == args->consumer->statsd_port) { sockfd_put(sock); return 2000; } else { if (lookahead_size >= 5) { if (*(u32 *)buf == g_http_get_intval || *(u32 *)buf == g_http_post_intval || *(u32 *)buf == g_http_put_intval || *(u32 *)buf == g_http_delete_intval || *(u32 *)buf == g_http_trace_intval || *(u32 *)buf == g_http_connect_intval || *(u32 *)buf == g_http_options_intval || ((*(u32 *)buf == g_http_resp_intval) && (buf[4] == '/')) ) { sockfd_put(sock); return 2000; } } } } } } sockfd_put(sock); } return res; } /* * NOTES: * - val_len is ignored for everything other than PT_BYTEBUF. * - fromuser is ignored for numeric types * - dyn_idx is ignored for everything other than PT_DYN */ int val_to_ring(struct event_filler_arguments *args, uint64_t val, u32 val_len, bool fromuser, u8 dyn_idx) { const struct ppm_param_info *param_info; int len = -1; u16 *psize = (u16 *)(args->buffer + args->curarg * sizeof(u16)); u32 max_arg_size = args->arg_data_size; if (unlikely(args->curarg >= args->nargs)) { pr_err("(%u)val_to_ring: too many arguments for event #%u, type=%u, curarg=%u, nargs=%u tid:%u\n", smp_processor_id(), args->nevents, (u32)args->event_type, args->curarg, args->nargs, current->pid); memory_dump(args->buffer - sizeof(struct ppm_evt_hdr), 32); ASSERT(0); return PPM_FAILURE_BUG; } if (unlikely(args->arg_data_size == 0)) return PPM_FAILURE_BUFFER_FULL; if (max_arg_size > PPM_MAX_ARG_SIZE) max_arg_size = PPM_MAX_ARG_SIZE; param_info = &(g_event_info[args->event_type].params[args->curarg]); if (param_info->type == PT_DYN && param_info->info != NULL) { const struct ppm_param_info *dyn_params; if (unlikely(dyn_idx >= param_info->ninfo)) { ASSERT(0); return PPM_FAILURE_BUG; } dyn_params = (const struct ppm_param_info *)param_info->info; param_info = &dyn_params[dyn_idx]; if (likely(max_arg_size >= sizeof(u8))) { *(u8 *)(args->buffer + args->arg_data_offset) = dyn_idx; len = sizeof(u8); } else { return PPM_FAILURE_BUFFER_FULL; } args->arg_data_offset += len; args->arg_data_size -= len; max_arg_size -= len; *psize = (u16)len; } else { *psize = 0; } switch (param_info->type) { case PT_CHARBUF: case PT_FSPATH: if (likely(val != 0)) { if (fromuser) { len = ppm_strncpy_from_user(args->buffer + args->arg_data_offset, (const char __user *)(unsigned long)val, max_arg_size); if (unlikely(len < 0)) return PPM_FAILURE_INVALID_USER_MEMORY; } else { len = strlcpy(args->buffer + args->arg_data_offset, (const char *)(unsigned long)val, max_arg_size); if (++len > max_arg_size) len = max_arg_size; } /* * Make sure the string is null-terminated */ *(char *)(args->buffer + args->arg_data_offset + len) = 0; } else { /* * Handle NULL pointers */ len = strlcpy(args->buffer + args->arg_data_offset, "(NULL)", max_arg_size); if (++len > max_arg_size) len = max_arg_size; } break; case PT_BYTEBUF: if (likely(val != 0)) { if (fromuser) { /* * Copy the lookahead portion of the buffer that we will use DPI-based * snaplen calculation */ u32 dpi_lookahead_size = DPI_LOOKAHEAD_SIZE; if (dpi_lookahead_size > val_len) dpi_lookahead_size = val_len; if (unlikely(dpi_lookahead_size >= max_arg_size)) return PPM_FAILURE_BUFFER_FULL; len = (int)ppm_copy_from_user(args->buffer + args->arg_data_offset, (const void __user *)(unsigned long)val, dpi_lookahead_size); if (unlikely(len != 0)) return PPM_FAILURE_INVALID_USER_MEMORY; /* * Check if there's more to copy */ if (likely((dpi_lookahead_size != val_len))) { /* * Calculate the snaplen */ if (likely(args->enforce_snaplen)) { u32 sl = args->consumer->snaplen; sl = compute_snaplen(args, args->buffer + args->arg_data_offset, dpi_lookahead_size); if (val_len > sl) val_len = sl; } if (unlikely((val_len) >= max_arg_size)) val_len = max_arg_size; if (val_len > dpi_lookahead_size) { len = (int)ppm_copy_from_user(args->buffer + args->arg_data_offset + dpi_lookahead_size, (const void __user *)(unsigned long)val + dpi_lookahead_size, val_len - dpi_lookahead_size); if (unlikely(len != 0)) return PPM_FAILURE_INVALID_USER_MEMORY; } } len = val_len; } else { if (likely(args->enforce_snaplen)) { u32 sl = compute_snaplen(args, (char *)(unsigned long)val, val_len); if (val_len > sl) val_len = sl; } if (unlikely(val_len >= max_arg_size)) return PPM_FAILURE_BUFFER_FULL; memcpy(args->buffer + args->arg_data_offset, (void *)(unsigned long)val, val_len); len = val_len; } } else { /* * Handle NULL pointers */ len = 0; } break; case PT_SOCKADDR: case PT_SOCKTUPLE: case PT_FDLIST: if (likely(val != 0)) { if (unlikely(val_len >= max_arg_size)) return PPM_FAILURE_BUFFER_FULL; if (fromuser) { len = (int)ppm_copy_from_user(args->buffer + args->arg_data_offset, (const void __user *)(unsigned long)val, val_len); if (unlikely(len != 0)) return PPM_FAILURE_INVALID_USER_MEMORY; len = val_len; } else { memcpy(args->buffer + args->arg_data_offset, (void *)(unsigned long)val, val_len); len = val_len; } } else { /* * Handle NULL pointers */ len = 0; } break; case PT_FLAGS8: case PT_UINT8: case PT_SIGTYPE: if (likely(max_arg_size >= sizeof(u8))) { *(u8 *)(args->buffer + args->arg_data_offset) = (u8)val; len = sizeof(u8); } else { return PPM_FAILURE_BUFFER_FULL; } break; case PT_FLAGS16: case PT_UINT16: case PT_SYSCALLID: if (likely(max_arg_size >= sizeof(u16))) { *(u16 *)(args->buffer + args->arg_data_offset) = (u16)val; len = sizeof(u16); } else { return PPM_FAILURE_BUFFER_FULL; } break; case PT_FLAGS32: case PT_UINT32: case PT_MODE: case PT_UID: case PT_GID: case PT_SIGSET: if (likely(max_arg_size >= sizeof(u32))) { *(u32 *)(args->buffer + args->arg_data_offset) = (u32)val; len = sizeof(u32); } else { return PPM_FAILURE_BUFFER_FULL; } break; case PT_RELTIME: case PT_ABSTIME: case PT_UINT64: if (likely(max_arg_size >= sizeof(u64))) { *(u64 *)(args->buffer + args->arg_data_offset) = (u64)val; len = sizeof(u64); } else { return PPM_FAILURE_BUFFER_FULL; } break; case PT_INT8: if (likely(max_arg_size >= sizeof(s8))) { *(s8 *)(args->buffer + args->arg_data_offset) = (s8)(long)val; len = sizeof(s8); } else { return PPM_FAILURE_BUFFER_FULL; } break; case PT_INT16: if (likely(max_arg_size >= sizeof(s16))) { *(s16 *)(args->buffer + args->arg_data_offset) = (s16)(long)val; len = sizeof(s16); } else { return PPM_FAILURE_BUFFER_FULL; } break; case PT_INT32: if (likely(max_arg_size >= sizeof(s32))) { *(s32 *)(args->buffer + args->arg_data_offset) = (s32)(long)val; len = sizeof(s32); } else { return PPM_FAILURE_BUFFER_FULL; } break; case PT_INT64: case PT_ERRNO: case PT_FD: case PT_PID: if (likely(max_arg_size >= sizeof(s64))) { *(s64 *)(args->buffer + args->arg_data_offset) = (s64)(long)val; len = sizeof(s64); } else { return PPM_FAILURE_BUFFER_FULL; } break; default: ASSERT(0); pr_err("val_to_ring: invalid argument type %d. Event %u (%s) might have less parameters than what has been declared in nparams\n", (int)g_event_info[args->event_type].params[args->curarg].type, (u32)args->event_type, g_event_info[args->event_type].name); return PPM_FAILURE_BUG; } ASSERT(len <= PPM_MAX_ARG_SIZE); ASSERT(len <= max_arg_size); *psize += (u16)len; args->curarg++; args->arg_data_offset += len; args->arg_data_size -= len; return PPM_SUCCESS; } /* static struct socket *ppm_sockfd_lookup_light(int fd, int *err, int *fput_needed) { struct file *file; struct socket *sock; *err = -EBADF; file = fget_light(fd, fput_needed); if (file) { sock = sock_from_file(file, err); if (sock) return sock; fput_light(file, *fput_needed); } return NULL; } */ /* * Convert a sockaddr into our address representation and copy it to * targetbuf */ u16 pack_addr(struct sockaddr *usrsockaddr, int ulen, char *targetbuf, u16 targetbufsize) { u32 ip; u16 port; sa_family_t family = usrsockaddr->sa_family; struct sockaddr_in *usrsockaddr_in; struct sockaddr_in6 *usrsockaddr_in6; struct sockaddr_un *usrsockaddr_un; u16 size; char *dest; switch (family) { case AF_INET: /* * Map the user-provided address to a sockaddr_in */ usrsockaddr_in = (struct sockaddr_in *)usrsockaddr; /* * Retrieve the src address */ ip = usrsockaddr_in->sin_addr.s_addr; port = ntohs(usrsockaddr_in->sin_port); /* * Pack the tuple info in the temporary buffer */ size = 1 + 4 + 2; /* family + ip + port */ *targetbuf = socket_family_to_scap(family); *(u32 *)(targetbuf + 1) = ip; *(u16 *)(targetbuf + 5) = port; break; case AF_INET6: /* * Map the user-provided address to a sockaddr_in */ usrsockaddr_in6 = (struct sockaddr_in6 *)usrsockaddr; /* * Retrieve the src address */ port = ntohs(usrsockaddr_in6->sin6_port); /* * Pack the tuple info in the temporary buffer */ size = 1 + 16 + 2; /* family + ip + port */ *targetbuf = socket_family_to_scap(family); memcpy(targetbuf + 1, usrsockaddr_in6->sin6_addr.s6_addr, 16); *(u16 *)(targetbuf + 17) = port; break; case AF_UNIX: /* * Map the user-provided address to a sockaddr_in */ usrsockaddr_un = (struct sockaddr_un *)usrsockaddr; /* * Put a 0 at the end of struct sockaddr_un because * the user might not have considered it in the length */ if (ulen == sizeof(struct sockaddr_storage)) *(((char *)usrsockaddr_un) + ulen - 1) = 0; else *(((char *)usrsockaddr_un) + ulen) = 0; /* * Pack the data into the target buffer */ size = 1; *targetbuf = socket_family_to_scap(family); dest = strncpy(targetbuf + 1, usrsockaddr_un->sun_path, UNIX_PATH_MAX); /* we assume this will be smaller than (targetbufsize - (1 + 8 + 8)) */ dest[UNIX_PATH_MAX - 1] = 0; size += strlen(dest) + 1; break; default: size = 0; break; } return size; } /* * Convert a connection tuple into our tuple representation and copy it to * targetbuf */ u16 fd_to_socktuple(int fd, struct sockaddr *usrsockaddr, int ulen, bool use_userdata, bool is_inbound, char *targetbuf, u16 targetbufsize) { struct socket *sock; int err = 0; sa_family_t family; struct unix_sock *us; char *us_name; struct sock *speer; u32 sip; u32 dip; u8 *sip6; u8 *dip6; u16 sport; u16 dport; struct sockaddr_in *usrsockaddr_in; struct sockaddr_in6 *usrsockaddr_in6; struct sockaddr_un *usrsockaddr_un; u16 size; char *dest; struct sockaddr_storage sock_address; struct sockaddr_storage peer_address; /* * Get the socket from the fd * NOTE: sockfd_lookup() locks the socket, so we don't need to worry when we dig in it */ sock = sockfd_lookup(fd, &err); if (unlikely(!sock || !(sock->sk))) { /* * This usually happens if the call failed without being able to establish a connection, * i.e. if it didn't return something like SE_EINPROGRESS. */ if (sock) sockfd_put(sock); return 0; } err = sock_getname(sock, (struct sockaddr *)&sock_address, 0); ASSERT(err == 0); family = sock->sk->sk_family; /* * Extract and pack the info, based on the family */ switch (family) { case AF_INET: if (!use_userdata) { err = sock_getname(sock, (struct sockaddr *)&peer_address, 1); if (err == 0) { if (is_inbound) { sip = ((struct sockaddr_in *) &peer_address)->sin_addr.s_addr; sport = ntohs(((struct sockaddr_in *) &peer_address)->sin_port); dip = ((struct sockaddr_in *) &sock_address)->sin_addr.s_addr; dport = ntohs(((struct sockaddr_in *) &sock_address)->sin_port); } else { sip = ((struct sockaddr_in *) &sock_address)->sin_addr.s_addr; sport = ntohs(((struct sockaddr_in *) &sock_address)->sin_port); dip = ((struct sockaddr_in *) &peer_address)->sin_addr.s_addr; dport = ntohs(((struct sockaddr_in *) &peer_address)->sin_port); } } else { sip = 0; sport = 0; dip = 0; dport = 0; } } else { /* * Map the user-provided address to a sockaddr_in */ usrsockaddr_in = (struct sockaddr_in *)usrsockaddr; if (is_inbound) { sip = usrsockaddr_in->sin_addr.s_addr; sport = ntohs(usrsockaddr_in->sin_port); dip = ((struct sockaddr_in *) &sock_address)->sin_addr.s_addr; dport = ntohs(((struct sockaddr_in *) &sock_address)->sin_port); } else { sip = ((struct sockaddr_in *) &sock_address)->sin_addr.s_addr; sport = ntohs(((struct sockaddr_in *) &sock_address)->sin_port); dip = usrsockaddr_in->sin_addr.s_addr; dport = ntohs(usrsockaddr_in->sin_port); } } /* * Pack the tuple info in the temporary buffer */ size = 1 + 4 + 4 + 2 + 2; /* family + sip + dip + sport + dport */ *targetbuf = socket_family_to_scap(family); *(u32 *)(targetbuf + 1) = sip; *(u16 *)(targetbuf + 5) = sport; *(u32 *)(targetbuf + 7) = dip; *(u16 *)(targetbuf + 11) = dport; break; case AF_INET6: if (!use_userdata) { err = sock_getname(sock, (struct sockaddr *)&peer_address, 1); ASSERT(err == 0); if (is_inbound) { sip6 = ((struct sockaddr_in6 *) &peer_address)->sin6_addr.s6_addr; sport = ntohs(((struct sockaddr_in6 *) &peer_address)->sin6_port); dip6 = ((struct sockaddr_in6 *) &sock_address)->sin6_addr.s6_addr; dport = ntohs(((struct sockaddr_in6 *) &sock_address)->sin6_port); } else { sip6 = ((struct sockaddr_in6 *) &sock_address)->sin6_addr.s6_addr; sport = ntohs(((struct sockaddr_in6 *) &sock_address)->sin6_port); dip6 = ((struct sockaddr_in6 *) &peer_address)->sin6_addr.s6_addr; dport = ntohs(((struct sockaddr_in6 *) &peer_address)->sin6_port); } } else { /* * Map the user-provided address to a sockaddr_in6 */ usrsockaddr_in6 = (struct sockaddr_in6 *)usrsockaddr; if (is_inbound) { sip6 = usrsockaddr_in6->sin6_addr.s6_addr; sport = ntohs(usrsockaddr_in6->sin6_port); dip6 = ((struct sockaddr_in6 *) &sock_address)->sin6_addr.s6_addr; dport = ntohs(((struct sockaddr_in6 *) &sock_address)->sin6_port); } else { sip6 = ((struct sockaddr_in6 *) &sock_address)->sin6_addr.s6_addr; sport = ntohs(((struct sockaddr_in6 *) &sock_address)->sin6_port); dip6 = usrsockaddr_in6->sin6_addr.s6_addr; dport = ntohs(usrsockaddr_in6->sin6_port); } } /* * Pack the tuple info in the temporary buffer */ size = 1 + 16 + 16 + 2 + 2; /* family + sip + dip + sport + dport */ *targetbuf = socket_family_to_scap(family); memcpy(targetbuf + 1, sip6, 16); *(u16 *)(targetbuf + 17) = sport; memcpy(targetbuf + 19, dip6, 16); *(u16 *)(targetbuf + 35) = dport; break; case AF_UNIX: /* * Retrieve the addresses */ us = unix_sk(sock->sk); speer = us->peer; *targetbuf = socket_family_to_scap(family); if (is_inbound) { *(uint64_t *)(targetbuf + 1) = (uint64_t)(unsigned long)us; *(uint64_t *)(targetbuf + 1 + 8) = (uint64_t)(unsigned long)speer; } else { *(uint64_t *)(targetbuf + 1) = (uint64_t)(unsigned long)speer; *(uint64_t *)(targetbuf + 1 + 8) = (uint64_t)(unsigned long)us; } /* * Pack the data into the target buffer */ size = 1 + 8 + 8; if (!use_userdata) { if (is_inbound) { us_name = ((struct sockaddr_un *) &sock_address)->sun_path; } else { err = sock_getname(sock, (struct sockaddr *)&peer_address, 1); ASSERT(err == 0); us_name = ((struct sockaddr_un *) &peer_address)->sun_path; } } else { /* * Map the user-provided address to a sockaddr_in */ usrsockaddr_un = (struct sockaddr_un *)usrsockaddr; /* * Put a 0 at the end of struct sockaddr_un because * the user might not have considered it in the length */ if (ulen == sizeof(struct sockaddr_storage)) *(((char *)usrsockaddr_un) + ulen - 1) = 0; else *(((char *)usrsockaddr_un) + ulen) = 0; if (is_inbound) us_name = ((struct sockaddr_un *) &sock_address)->sun_path; else us_name = usrsockaddr_un->sun_path; } ASSERT(us_name); dest = strncpy(targetbuf + 1 + 8 + 8, (char *)us_name, UNIX_PATH_MAX); /* we assume this will be smaller than (targetbufsize - (1 + 8 + 8)) */ dest[UNIX_PATH_MAX - 1] = 0; size += strlen(dest) + 1; break; default: size = 0; break; } /* * Digging finished. We can release the fd. */ sockfd_put(sock); return size; } int addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr) { if (unlikely(ulen < 0 || ulen > sizeof(struct sockaddr_storage))) return -EINVAL; if (unlikely(ulen == 0)) return 0; if (unlikely(ppm_copy_from_user(kaddr, uaddr, ulen))) return -EFAULT; return 0; } /* * Parses the list of buffers of a xreadv or xwritev call, and pushes the size * (and optionally the data) to the ring. */ int32_t parse_readv_writev_bufs(struct event_filler_arguments *args, const struct iovec __user *iovsrc, unsigned long iovcnt, int64_t retval, int flags) { int32_t res; const struct iovec *iov; u64 copylen; u32 j; u64 size = 0; unsigned long bufsize; char *targetbuf = args->str_storage; u32 targetbuflen = STR_STORAGE_SIZE; unsigned long syscall_args[6] = {}; unsigned long val; u32 notcopied_len; size_t tocopy_len; copylen = iovcnt * sizeof(struct iovec); if (unlikely(iovcnt >= 0xffffffff)) return PPM_FAILURE_BUFFER_FULL; if (unlikely(copylen >= STR_STORAGE_SIZE)) return PPM_FAILURE_BUFFER_FULL; if (unlikely(ppm_copy_from_user(args->str_storage, iovsrc, copylen))) return PPM_FAILURE_INVALID_USER_MEMORY; iov = (const struct iovec *)(args->str_storage); targetbuf += copylen; targetbuflen -= copylen; /* * Size */ if (flags & PRB_FLAG_PUSH_SIZE) { for (j = 0; j < iovcnt; j++) size += iov[j].iov_len; /* * Size is the total size of the buffers provided by the user. The number of * received bytes can be smaller */ if ((flags & PRB_FLAG_IS_WRITE) == 0) if (size > retval) size = retval; res = val_to_ring(args, size, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } /* * data */ if (flags & PRB_FLAG_PUSH_DATA) { if (retval > 0 && iovcnt > 0) { /* * Retrieve the FD. It will be used for dynamic snaplen calculation. */ if (!args->is_socketcall) { ppm_syscall_get_arguments(current, args->regs, syscall_args); val = syscall_args[0]; } else val = args->socketcall_args[0]; args->fd = (int)val; /* * Merge the buffers */ bufsize = 0; for (j = 0; j < iovcnt; j++) { if ((flags & PRB_FLAG_IS_WRITE) == 0) { if (bufsize >= retval) { ASSERT(bufsize >= retval); /* * Copied all the data even if we haven't reached the * end of the buffer. * Copy must stop here. */ break; } tocopy_len = min(iov[j].iov_len, (size_t)retval - bufsize); tocopy_len = min(tocopy_len, (size_t)targetbuflen - bufsize - 1); } else { tocopy_len = min(iov[j].iov_len, targetbuflen - bufsize - 1); } notcopied_len = (int)ppm_copy_from_user(targetbuf + bufsize, iov[j].iov_base, tocopy_len); if (unlikely(notcopied_len != 0)) { /* * This means we had a page fault. Skip this event. */ return PPM_FAILURE_INVALID_USER_MEMORY; } bufsize += tocopy_len; if (tocopy_len != iov[j].iov_len) { /* * No space left in the args->str_storage buffer. * Copy must stop here. */ break; } } args->enforce_snaplen = true; res = val_to_ring(args, (unsigned long)targetbuf, bufsize, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } else { res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } } return PPM_SUCCESS; } #ifdef CONFIG_COMPAT /* * Parses the list of buffers of a xreadv or xwritev call, and pushes the size * (and optionally the data) to the ring. */ int32_t compat_parse_readv_writev_bufs(struct event_filler_arguments *args, const struct compat_iovec __user *iovsrc, unsigned long iovcnt, int64_t retval, int flags) { int32_t res; const struct compat_iovec *iov; u64 copylen; u32 j; u64 size = 0; unsigned long bufsize; char *targetbuf = args->str_storage; u32 targetbuflen = STR_STORAGE_SIZE; unsigned long syscall_args[6] = {}; unsigned long val; u32 notcopied_len; compat_size_t tocopy_len; copylen = iovcnt * sizeof(struct compat_iovec); if (unlikely(iovcnt >= 0xffffffff)) return PPM_FAILURE_BUFFER_FULL; if (unlikely(copylen >= STR_STORAGE_SIZE)) return PPM_FAILURE_BUFFER_FULL; if (unlikely(ppm_copy_from_user(args->str_storage, iovsrc, copylen))) return PPM_FAILURE_INVALID_USER_MEMORY; iov = (const struct compat_iovec *)(args->str_storage); targetbuf += copylen; targetbuflen -= copylen; /* * Size */ if (flags & PRB_FLAG_PUSH_SIZE) { for (j = 0; j < iovcnt; j++) size += iov[j].iov_len; /* * Size is the total size of the buffers provided by the user. The number of * received bytes can be smaller */ if ((flags & PRB_FLAG_IS_WRITE) == 0) if (size > retval) size = retval; res = val_to_ring(args, size, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } /* * data */ if (flags & PRB_FLAG_PUSH_DATA) { if (retval > 0 && iovcnt > 0) { /* * Retrieve the FD. It will be used for dynamic snaplen calculation. */ if (!args->is_socketcall) { ppm_syscall_get_arguments(current, args->regs, syscall_args); val = syscall_args[0]; } else val = args->socketcall_args[0]; args->fd = (int)val; /* * Merge the buffers */ bufsize = 0; for (j = 0; j < iovcnt; j++) { if ((flags & PRB_FLAG_IS_WRITE) == 0) { if (bufsize >= retval) { ASSERT(bufsize >= retval); /* * Copied all the data even if we haven't reached the * end of the buffer. * Copy must stop here. */ break; } tocopy_len = min(iov[j].iov_len, (compat_size_t)((size_t)retval - bufsize)); tocopy_len = min(tocopy_len, (compat_size_t)(targetbuflen - bufsize - 1)); } else { tocopy_len = min(iov[j].iov_len, (compat_size_t)(targetbuflen - bufsize - 1)); } notcopied_len = (int)ppm_copy_from_user(targetbuf + bufsize, compat_ptr(iov[j].iov_base), tocopy_len); if (unlikely(notcopied_len != 0)) { /* * This means we had a page fault. Skip this event. */ return PPM_FAILURE_INVALID_USER_MEMORY; } bufsize += tocopy_len; if (tocopy_len != iov[j].iov_len) { /* * No space left in the args->str_storage buffer. * Copy must stop here. */ break; } } args->enforce_snaplen = true; res = val_to_ring(args, (unsigned long)targetbuf, bufsize, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } else { res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } } return PPM_SUCCESS; } #endif /* CONFIG_COMPAT */ /* * STANDARD FILLERS */ /* * AUTOFILLER * In simple cases in which extracting an event is just a matter of moving the * arguments to the buffer, this filler can be used instead of writing a * filler function. * The arguments to extract are be specified in g_ppm_events. */ int f_sys_autofill(struct event_filler_arguments *args) { int res; unsigned long syscall_args[6] = {}; unsigned long val; u32 j; int64_t retval; const struct ppm_event_entry *evinfo = &g_ppm_events[args->event_type]; ASSERT(evinfo->n_autofill_args <= PPM_MAX_AUTOFILL_ARGS); for (j = 0; j < evinfo->n_autofill_args; j++) { if (evinfo->autofill_args[j].id >= 0) { #ifdef _HAS_SOCKETCALL if (args->is_socketcall && evinfo->paramtype == APT_SOCK) { val = args->socketcall_args[evinfo->autofill_args[j].id]; } else #endif { /* * Regular argument */ ppm_syscall_get_arguments(current, args->regs, syscall_args); val = syscall_args[evinfo->autofill_args[j].id]; } res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; } else if (evinfo->autofill_args[j].id == AF_ID_RETVAL) { /* * Return value */ retval = (int64_t)(long)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } else if (evinfo->autofill_args[j].id == AF_ID_USEDEFAULT) { /* * Default Value */ res = val_to_ring(args, evinfo->autofill_args[j].default_val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } else { ASSERT(false); } } return add_sentinel(args); } sysdig-0.26.4/driver/ppm_events.h000066400000000000000000000071051352731327100167400ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifndef EVENTS_H_ #define EVENTS_H_ /* To know about __NR_socketcall */ #include #ifdef CONFIG_COMPAT #include #endif #ifdef __NR_socketcall #define _HAS_SOCKETCALL #endif #if defined(CONFIG_X86_64) && defined(CONFIG_IA32_EMULATION) #define _HAS_SOCKETCALL #endif #include "ppm_events_public.h" /* * Various crap that a callback might need */ struct fault_data_t { unsigned long address; struct pt_regs *regs; unsigned long error_code; }; struct event_filler_arguments { struct ppm_consumer_t *consumer; char *buffer; /* the buffer that will be filled with the data */ u32 buffer_size; /* the space in the ring buffer available for this event */ u32 syscall_id; /* the system call ID */ const enum ppm_syscall_code *cur_g_syscall_code_routing_table; #ifdef PPM_ENABLE_SENTINEL u32 sentinel; #endif u32 nevents; u32 curarg; u32 nargs; u32 arg_data_offset; u32 arg_data_size; enum ppm_event_type event_type; /* the event type */ /* Eventually convert this to an event_info union and move all the * below per-event params in this union, it's not good to waste kernel * stack since all this stuff is always exclusive */ struct pt_regs *regs; /* the registers containing the call arguments */ struct task_struct *sched_prev; /* for context switch events, the task that is being scheduled out */ struct task_struct *sched_next; /* for context switch events, the task that is being scheduled in */ char *str_storage; /* String storage. Size is one page. */ unsigned long socketcall_args[6]; bool is_socketcall; int socketcall_syscall; bool compat; int fd; /* Passed by some of the fillers to val_to_ring to compute the snaplen dynamically */ bool enforce_snaplen; int signo; /* Signal number */ __kernel_pid_t spid; /* PID of source process */ __kernel_pid_t dpid; /* PID of destination process */ struct fault_data_t fault_data; /* For page faults */ }; extern const struct ppm_event_entry g_ppm_events[]; /* * HTTP markers */ #define HTTP_GET_STR "GET " #define HTTP_OPTIONS_STR "OPTI" #define HTTP_HEAD_STR "HEAD" #define HTTP_POST_STR "POST" #define HTTP_PUT_STR "PUT " #define HTTP_DELETE_STR "DELE" #define HTTP_TRACE_STR "TRAC" #define HTTP_CONNECT_STR "CONN" #define HTTP_RESP_STR "HTTP" /* * Functions */ int32_t dpi_lookahead_init(void); int32_t val_to_ring(struct event_filler_arguments *args, u64 val, u32 val_len, bool fromuser, u8 dyn_idx); u16 pack_addr(struct sockaddr *usrsockaddr, int ulen, char *targetbuf, u16 targetbufsize); u16 fd_to_socktuple(int fd, struct sockaddr *usrsockaddr, int ulen, bool use_userdata, bool is_inbound, char *targetbuf, u16 targetbufsize); int addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr); int32_t parse_readv_writev_bufs(struct event_filler_arguments *args, const struct iovec __user *iovsrc, unsigned long iovcnt, int64_t retval, int flags); #ifdef CONFIG_COMPAT int32_t compat_parse_readv_writev_bufs(struct event_filler_arguments *args, const struct compat_iovec __user *iovsrc, unsigned long iovcnt, int64_t retval, int flags); #endif static inline int add_sentinel(struct event_filler_arguments *args) { #ifdef PPM_ENABLE_SENTINEL if (likely(args->arg_data_size >= sizeof(u32))) { *(u32 *)(args->buffer + args->arg_data_offset) = args->sentinel; args->arg_data_offset += 4; args->arg_data_size -= 4; return PPM_SUCCESS; } return PPM_FAILURE_BUFFER_FULL; #else return PPM_SUCCESS; #endif } #endif /* EVENTS_H_ */ sysdig-0.26.4/driver/ppm_events_public.h000066400000000000000000001406051352731327100203010ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifndef EVENTS_PUBLIC_H_ #define EVENTS_PUBLIC_H_ #if defined(__sun) #include #endif #ifdef __KERNEL__ #include #else #include "../userspace/common/sysdig_types.h" #endif /* * Macros for packing in different build environments */ #if !defined(CYGWING_AGENT) && (defined(_WIN64) || defined(WIN64) || defined(_WIN32) || defined(WIN32)) #define _packed __pragma(pack(push, 1)); __pragma(pack(pop)) #else #define _packed __attribute__((packed)) #endif /* * Limits */ #define PPM_MAX_EVENT_PARAMS (1 << 5) /* Max number of parameters an event can have */ #define PPM_MAX_PATH_SIZE 256 /* Max size that an event parameter can have in the circular buffer, in bytes */ #define PPM_MAX_NAME_LEN 32 /* * Socket families */ #define PPM_AF_UNSPEC 0 #define PPM_AF_UNIX 1 /* Unix domain sockets */ #define PPM_AF_LOCAL 1 /* POSIX name for PPM_AF_UNIX */ #define PPM_AF_INET 2 /* Internet IP Protocol */ #define PPM_AF_AX25 3 /* Amateur Radio AX.25 */ #define PPM_AF_IPX 4 /* Novell IPX */ #define PPM_AF_APPLETALK 5 /* AppleTalk DDP */ #define PPM_AF_NETROM 6 /* Amateur Radio NET/ROM */ #define PPM_AF_BRIDGE 7 /* Multiprotocol bridge */ #define PPM_AF_ATMPVC 8 /* ATM PVCs */ #define PPM_AF_X25 9 /* Reserved for X.25 project */ #define PPM_AF_INET6 10 /* IP version 6 */ #define PPM_AF_ROSE 11 /* Amateur Radio X.25 PLP */ #define PPM_AF_DECnet 12 /* Reserved for DECnet project */ #define PPM_AF_NETBEUI 13 /* Reserved for 802.2LLC project*/ #define PPM_AF_SECURITY 14 /* Security callback pseudo AF */ #define PPM_AF_KEY 15 /* PF_KEY key management API */ #define PPM_AF_NETLINK 16 #define PPM_AF_ROUTE PPM_AF_NETLINK /* Alias to emulate 4.4BSD */ #define PPM_AF_PACKET 17 /* Packet family */ #define PPM_AF_ASH 18 /* Ash */ #define PPM_AF_ECONET 19 /* Acorn Econet */ #define PPM_AF_ATMSVC 20 /* ATM SVCs */ #define PPM_AF_RDS 21 /* RDS sockets */ #define PPM_AF_SNA 22 /* Linux SNA Project (nutters!) */ #define PPM_AF_IRDA 23 /* IRDA sockets */ #define PPM_AF_PPPOX 24 /* PPPoX sockets */ #define PPM_AF_WANPIPE 25 /* Wanpipe API Sockets */ #define PPM_AF_LLC 26 /* Linux LLC */ #define PPM_AF_CAN 29 /* Controller Area Network */ #define PPM_AF_TIPC 30 /* TIPC sockets */ #define PPM_AF_BLUETOOTH 31 /* Bluetooth sockets */ #define PPM_AF_IUCV 32 /* IUCV sockets */ #define PPM_AF_RXRPC 33 /* RxRPC sockets */ #define PPM_AF_ISDN 34 /* mISDN sockets */ #define PPM_AF_PHONET 35 /* Phonet sockets */ #define PPM_AF_IEEE802154 36 /* IEEE802154 sockets */ #define PPM_AF_CAIF 37 /* CAIF sockets */ #define PPM_AF_ALG 38 /* Algorithm sockets */ #define PPM_AF_NFC 39 /* NFC sockets */ /* * File flags */ #define PPM_O_NONE 0 #define PPM_O_RDONLY (1 << 0) /* Open for reading only */ #define PPM_O_WRONLY (1 << 1) /* Open for writing only */ #define PPM_O_RDWR (PPM_O_RDONLY | PPM_O_WRONLY) /* Open for reading and writing */ #define PPM_O_CREAT (1 << 2) /* Create a new file if it doesn't exist. */ #define PPM_O_APPEND (1 << 3) /* If set, the file offset shall be set to the end of the file prior to each write. */ #define PPM_O_DSYNC (1 << 4) #define PPM_O_EXCL (1 << 5) #define PPM_O_NONBLOCK (1 << 6) #define PPM_O_SYNC (1 << 7) #define PPM_O_TRUNC (1 << 8) #define PPM_O_DIRECT (1 << 9) #define PPM_O_DIRECTORY (1 << 10) #define PPM_O_LARGEFILE (1 << 11) #define PPM_O_CLOEXEC (1 << 12) /* * File modes */ #define PPM_S_NONE 0 #define PPM_S_IXOTH (1 << 0) #define PPM_S_IWOTH (1 << 1) #define PPM_S_IROTH (1 << 2) #define PPM_S_IXGRP (1 << 3) #define PPM_S_IWGRP (1 << 4) #define PPM_S_IRGRP (1 << 5) #define PPM_S_IXUSR (1 << 6) #define PPM_S_IWUSR (1 << 7) #define PPM_S_IRUSR (1 << 8) #define PPM_S_ISVTX (1 << 9) #define PPM_S_ISGID (1 << 10) #define PPM_S_ISUID (1 << 11) /* * flock() flags */ #define PPM_LOCK_NONE 0 #define PPM_LOCK_SH (1 << 0) #define PPM_LOCK_EX (1 << 1) #define PPM_LOCK_NB (1 << 2) #define PPM_LOCK_UN (1 << 3) /* * Clone flags */ #define PPM_CL_NONE 0 #define PPM_CL_CLONE_FILES (1 << 0) #define PPM_CL_CLONE_FS (1 << 1) #define PPM_CL_CLONE_IO (1 << 2) #define PPM_CL_CLONE_NEWIPC (1 << 3) #define PPM_CL_CLONE_NEWNET (1 << 4) #define PPM_CL_CLONE_NEWNS (1 << 5) #define PPM_CL_CLONE_NEWPID (1 << 6) #define PPM_CL_CLONE_NEWUTS (1 << 7) #define PPM_CL_CLONE_PARENT (1 << 8) #define PPM_CL_CLONE_PARENT_SETTID (1 << 9) #define PPM_CL_CLONE_PTRACE (1 << 10) #define PPM_CL_CLONE_SIGHAND (1 << 11) #define PPM_CL_CLONE_SYSVSEM (1 << 12) #define PPM_CL_CLONE_THREAD (1 << 13) #define PPM_CL_CLONE_UNTRACED (1 << 14) #define PPM_CL_CLONE_VM (1 << 15) #define PPM_CL_CLONE_INVERTED (1 << 16) /* libsinsp-specific flag. It's set if clone() returned in */ /* the child process before than in the parent process. */ #define PPM_CL_NAME_CHANGED (1 << 17) /* libsinsp-specific flag. Set when the thread name changes */ /* (for example because execve was called) */ #define PPM_CL_CLOSED (1 << 18) /* thread has been closed. */ #define PPM_CL_ACTIVE (1 << 19) /* libsinsp-specific flag. Set in the first non-clone event for this thread. */ #define PPM_CL_CLONE_NEWUSER (1 << 20) #define PPM_CL_PIPE_SRC (1 << 21) /* libsinsp-specific flag. Set if this thread has been detected to be the source in a shell pipe. */ #define PPM_CL_PIPE_DST (1 << 22) /* libsinsp-specific flag. Set if this thread has been detected to be the destination in a shell pipe. */ #define PPM_CL_CLONE_CHILD_CLEARTID (1 << 23) #define PPM_CL_CLONE_CHILD_SETTID (1 << 24) #define PPM_CL_CLONE_SETTLS (1 << 25) #define PPM_CL_CLONE_STOPPED (1 << 26) #define PPM_CL_CLONE_VFORK (1 << 27) #define PPM_CL_CLONE_NEWCGROUP (1 << 28) #define PPM_CL_CHILD_IN_PIDNS (1<<29) /* true if the thread created by clone() is *not* in the init pid namespace */ /* * Futex Operations */ #define PPM_FU_FUTEX_WAIT 0 #define PPM_FU_FUTEX_WAKE 1 #define PPM_FU_FUTEX_FD 2 #define PPM_FU_FUTEX_REQUEUE 3 #define PPM_FU_FUTEX_CMP_REQUEUE 4 #define PPM_FU_FUTEX_WAKE_OP 5 #define PPM_FU_FUTEX_LOCK_PI 6 #define PPM_FU_FUTEX_UNLOCK_PI 7 #define PPM_FU_FUTEX_TRYLOCK_PI 8 #define PPM_FU_FUTEX_WAIT_BITSET 9 #define PPM_FU_FUTEX_WAKE_BITSET 10 #define PPM_FU_FUTEX_WAIT_REQUEUE_PI 11 #define PPM_FU_FUTEX_CMP_REQUEUE_PI 12 #define PPM_FU_FUTEX_PRIVATE_FLAG 128 #define PPM_FU_FUTEX_CLOCK_REALTIME 256 /* * lseek() and llseek() whence */ #define PPM_SEEK_SET 0 #define PPM_SEEK_CUR 1 #define PPM_SEEK_END 2 /* * poll() flags */ #define PPM_POLLIN (1 << 0) #define PPM_POLLPRI (1 << 1) #define PPM_POLLOUT (1 << 2) #define PPM_POLLRDHUP (1 << 3) #define PPM_POLLERR (1 << 4) #define PPM_POLLHUP (1 << 5) #define PPM_POLLNVAL (1 << 6) #define PPM_POLLRDNORM (1 << 7) #define PPM_POLLRDBAND (1 << 8) #define PPM_POLLWRNORM (1 << 9) #define PPM_POLLWRBAND (1 << 10) /* * mount() flags */ #define PPM_MS_RDONLY (1<<0) #define PPM_MS_NOSUID (1<<1) #define PPM_MS_NODEV (1<<2) #define PPM_MS_NOEXEC (1<<3) #define PPM_MS_SYNCHRONOUS (1<<4) #define PPM_MS_REMOUNT (1<<5) #define PPM_MS_MANDLOCK (1<<6) #define PPM_MS_DIRSYNC (1<<7) #define PPM_MS_NOATIME (1<<10) #define PPM_MS_NODIRATIME (1<<11) #define PPM_MS_BIND (1<<12) #define PPM_MS_MOVE (1<<13) #define PPM_MS_REC (1<<14) #define PPM_MS_SILENT (1<<15) #define PPM_MS_POSIXACL (1<<16) #define PPM_MS_UNBINDABLE (1<<17) #define PPM_MS_PRIVATE (1<<18) #define PPM_MS_SLAVE (1<<19) #define PPM_MS_SHARED (1<<20) #define PPM_MS_RELATIME (1<<21) #define PPM_MS_KERNMOUNT (1<<22) #define PPM_MS_I_VERSION (1<<23) #define PPM_MS_STRICTATIME (1<<24) #define PPM_MS_LAZYTIME (1<<25) #define PPM_MS_NOSEC (1<<28) #define PPM_MS_BORN (1<<29) #define PPM_MS_ACTIVE (1<<30) #define PPM_MS_NOUSER (1<<31) /* * umount() flags */ #define PPM_MNT_FORCE 1 #define PPM_MNT_DETACH 2 #define PPM_MNT_EXPIRE 4 #define PPM_UMOUNT_NOFOLLOW 8 /* * shutdown() how */ #define PPM_SHUT_RD 0 #define PPM_SHUT_WR 1 #define PPM_SHUT_RDWR 2 /* * fs *at() flags */ #define PPM_AT_FDCWD -100 /* * unlinkat() flags */ #define PPM_AT_REMOVEDIR 0x200 /* * linkat() flags */ #define PPM_AT_SYMLINK_FOLLOW 0x400 #define PPM_AT_EMPTY_PATH 0x1000 /* * rlimit resources */ #define PPM_RLIMIT_CPU 0 /* CPU time in sec */ #define PPM_RLIMIT_FSIZE 1 /* Maximum filesize */ #define PPM_RLIMIT_DATA 2 /* max data size */ #define PPM_RLIMIT_STACK 3 /* max stack size */ #define PPM_RLIMIT_CORE 4 /* max core file size */ #define PPM_RLIMIT_RSS 5 /* max resident set size */ #define PPM_RLIMIT_NPROC 6 /* max number of processes */ #define PPM_RLIMIT_NOFILE 7 /* max number of open files */ #define PPM_RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */ #define PPM_RLIMIT_AS 9 /* address space limit */ #define PPM_RLIMIT_LOCKS 10 /* maximum file locks held */ #define PPM_RLIMIT_SIGPENDING 11 /* max number of pending signals */ #define PPM_RLIMIT_MSGQUEUE 12 /* maximum bytes in POSIX mqueues */ #define PPM_RLIMIT_NICE 13 /* max nice prio allowed to raise to 0-39 for nice level 19 .. -20 */ #define PPM_RLIMIT_RTPRIO 14 /* maximum realtime priority */ #define PPM_RLIMIT_RTTIME 15 /* timeout for RT tasks in us */ #define PPM_RLIMIT_UNKNOWN 255 /* CPU time in sec */ /* * fcntl commands */ #define PPM_FCNTL_UNKNOWN 0 #define PPM_FCNTL_F_DUPFD 1 #define PPM_FCNTL_F_GETFD 2 #define PPM_FCNTL_F_SETFD 3 #define PPM_FCNTL_F_GETFL 4 #define PPM_FCNTL_F_SETFL 5 #define PPM_FCNTL_F_GETLK 6 #define PPM_FCNTL_F_SETLK 8 #define PPM_FCNTL_F_SETLKW 9 #define PPM_FCNTL_F_SETOWN 10 #define PPM_FCNTL_F_GETOWN 12 #define PPM_FCNTL_F_SETSIG 13 #define PPM_FCNTL_F_GETSIG 15 #ifndef CONFIG_64BIT #define PPM_FCNTL_F_GETLK64 17 #define PPM_FCNTL_F_SETLK64 18 #define PPM_FCNTL_F_SETLKW64 19 #endif #define PPM_FCNTL_F_SETOWN_EX 21 #define PPM_FCNTL_F_GETOWN_EX 22 #define PPM_FCNTL_F_SETLEASE 23 #define PPM_FCNTL_F_GETLEASE 24 #define PPM_FCNTL_F_CANCELLK 25 #define PPM_FCNTL_F_DUPFD_CLOEXEC 26 #define PPM_FCNTL_F_NOTIFY 27 #define PPM_FCNTL_F_SETPIPE_SZ 28 #define PPM_FCNTL_F_GETPIPE_SZ 29 #define PPM_FCNTL_F_OFD_GETLK 30 #define PPM_FCNTL_F_OFD_SETLK 31 #define PPM_FCNTL_F_OFD_SETLKW 32 /* * getsockopt/setsockopt levels */ #define PPM_SOCKOPT_LEVEL_UNKNOWN 0 #define PPM_SOCKOPT_LEVEL_SOL_SOCKET 1 #define PPM_SOCKOPT_LEVEL_SOL_TCP 2 /* * getsockopt/setsockopt options * SOL_SOCKET only currently */ #define PPM_SOCKOPT_UNKNOWN 0 #define PPM_SOCKOPT_SO_DEBUG 1 #define PPM_SOCKOPT_SO_REUSEADDR 2 #define PPM_SOCKOPT_SO_TYPE 3 #define PPM_SOCKOPT_SO_ERROR 4 #define PPM_SOCKOPT_SO_DONTROUTE 5 #define PPM_SOCKOPT_SO_BROADCAST 6 #define PPM_SOCKOPT_SO_SNDBUF 7 #define PPM_SOCKOPT_SO_RCVBUF 8 #define PPM_SOCKOPT_SO_SNDBUFFORCE 32 #define PPM_SOCKOPT_SO_RCVBUFFORCE 33 #define PPM_SOCKOPT_SO_KEEPALIVE 9 #define PPM_SOCKOPT_SO_OOBINLINE 10 #define PPM_SOCKOPT_SO_NO_CHECK 11 #define PPM_SOCKOPT_SO_PRIORITY 12 #define PPM_SOCKOPT_SO_LINGER 13 #define PPM_SOCKOPT_SO_BSDCOMPAT 14 #define PPM_SOCKOPT_SO_REUSEPORT 15 #define PPM_SOCKOPT_SO_PASSCRED 16 #define PPM_SOCKOPT_SO_PEERCRED 17 #define PPM_SOCKOPT_SO_RCVLOWAT 18 #define PPM_SOCKOPT_SO_SNDLOWAT 19 #define PPM_SOCKOPT_SO_RCVTIMEO 20 #define PPM_SOCKOPT_SO_SNDTIMEO 21 #define PPM_SOCKOPT_SO_SECURITY_AUTHENTICATION 22 #define PPM_SOCKOPT_SO_SECURITY_ENCRYPTION_TRANSPORT 23 #define PPM_SOCKOPT_SO_SECURITY_ENCRYPTION_NETWORK 24 #define PPM_SOCKOPT_SO_BINDTODEVICE 25 #define PPM_SOCKOPT_SO_ATTACH_FILTER 26 #define PPM_SOCKOPT_SO_DETACH_FILTER 27 #define PPM_SOCKOPT_SO_PEERNAME 28 #define PPM_SOCKOPT_SO_TIMESTAMP 29 #define PPM_SOCKOPT_SO_ACCEPTCONN 30 #define PPM_SOCKOPT_SO_PEERSEC 31 #define PPM_SOCKOPT_SO_PASSSEC 34 #define PPM_SOCKOPT_SO_TIMESTAMPNS 35 #define PPM_SOCKOPT_SO_MARK 36 #define PPM_SOCKOPT_SO_TIMESTAMPING 37 #define PPM_SOCKOPT_SO_PROTOCOL 38 #define PPM_SOCKOPT_SO_DOMAIN 39 #define PPM_SOCKOPT_SO_RXQ_OVFL 40 #define PPM_SOCKOPT_SO_WIFI_STATUS 41 #define PPM_SOCKOPT_SO_PEEK_OFF 42 #define PPM_SOCKOPT_SO_NOFCS 43 #define PPM_SOCKOPT_SO_LOCK_FILTER 44 #define PPM_SOCKOPT_SO_SELECT_ERR_QUEUE 45 #define PPM_SOCKOPT_SO_BUSY_POLL 46 #define PPM_SOCKOPT_SO_MAX_PACING_RATE 47 #define PPM_SOCKOPT_SO_BPF_EXTENSIONS 48 #define PPM_SOCKOPT_SO_INCOMING_CPU 49 #define PPM_SOCKOPT_SO_ATTACH_BPF 50 #define PPM_SOCKOPT_SO_PEERGROUPS 51 #define PPM_SOCKOPT_SO_MEMINFO 52 #define PPM_SOCKOPT_SO_COOKIE 53 /* * getsockopt/setsockopt dynamic params */ #define PPM_SOCKOPT_IDX_UNKNOWN 0 #define PPM_SOCKOPT_IDX_ERRNO 1 #define PPM_SOCKOPT_IDX_UINT32 2 #define PPM_SOCKOPT_IDX_UINT64 3 #define PPM_SOCKOPT_IDX_TIMEVAL 4 #define PPM_SOCKOPT_IDX_MAX 5 /* * ptrace requests */ #define PPM_PTRACE_UNKNOWN 0 #define PPM_PTRACE_TRACEME 1 #define PPM_PTRACE_PEEKTEXT 2 #define PPM_PTRACE_PEEKDATA 3 #define PPM_PTRACE_PEEKUSR 4 #define PPM_PTRACE_POKETEXT 5 #define PPM_PTRACE_POKEDATA 6 #define PPM_PTRACE_POKEUSR 7 #define PPM_PTRACE_CONT 8 #define PPM_PTRACE_KILL 9 #define PPM_PTRACE_SINGLESTEP 10 #define PPM_PTRACE_ATTACH 11 #define PPM_PTRACE_DETACH 12 #define PPM_PTRACE_SYSCALL 13 #define PPM_PTRACE_SETOPTIONS 14 #define PPM_PTRACE_GETEVENTMSG 15 #define PPM_PTRACE_GETSIGINFO 16 #define PPM_PTRACE_SETSIGINFO 17 #define PPM_PTRACE_GETREGSET 18 #define PPM_PTRACE_SETREGSET 19 #define PPM_PTRACE_SEIZE 20 #define PPM_PTRACE_INTERRUPT 21 #define PPM_PTRACE_LISTEN 22 #define PPM_PTRACE_PEEKSIGINFO 23 #define PPM_PTRACE_GETSIGMASK 24 #define PPM_PTRACE_SETSIGMASK 25 #define PPM_PTRACE_GETREGS 26 #define PPM_PTRACE_SETREGS 27 #define PPM_PTRACE_GETFPREGS 28 #define PPM_PTRACE_SETFPREGS 29 #define PPM_PTRACE_GETFPXREGS 30 #define PPM_PTRACE_SETFPXREGS 31 #define PPM_PTRACE_OLDSETOPTIONS 32 #define PPM_PTRACE_GET_THREAD_AREA 33 #define PPM_PTRACE_SET_THREAD_AREA 34 #define PPM_PTRACE_ARCH_PRCTL 35 #define PPM_PTRACE_SYSEMU 36 #define PPM_PTRACE_SYSEMU_SINGLESTEP 37 #define PPM_PTRACE_SINGLEBLOCK 38 /* * ptrace dynamic table indexes */ #define PPM_PTRACE_IDX_UINT64 0 #define PPM_PTRACE_IDX_SIGTYPE 1 #define PPM_PTRACE_IDX_MAX 2 #define PPM_BPF_IDX_FD 0 #define PPM_BPF_IDX_RES 1 #define PPM_BPF_IDX_MAX 2 /* * memory protection flags */ #define PPM_PROT_NONE 0 #define PPM_PROT_READ (1 << 0) #define PPM_PROT_WRITE (1 << 1) #define PPM_PROT_EXEC (1 << 2) #define PPM_PROT_SEM (1 << 3) #define PPM_PROT_GROWSDOWN (1 << 4) #define PPM_PROT_GROWSUP (1 << 5) #define PPM_PROT_SAO (1 << 6) /* * mmap flags */ #define PPM_MAP_SHARED (1 << 0) #define PPM_MAP_PRIVATE (1 << 1) #define PPM_MAP_FIXED (1 << 2) #define PPM_MAP_ANONYMOUS (1 << 3) #define PPM_MAP_32BIT (1 << 4) #define PPM_MAP_RENAME (1 << 5) #define PPM_MAP_NORESERVE (1 << 6) #define PPM_MAP_POPULATE (1 << 7) #define PPM_MAP_NONBLOCK (1 << 8) #define PPM_MAP_GROWSDOWN (1 << 9) #define PPM_MAP_DENYWRITE (1 << 10) #define PPM_MAP_EXECUTABLE (1 << 11) #define PPM_MAP_INHERIT (1 << 12) #define PPM_MAP_FILE (1 << 13) #define PPM_MAP_LOCKED (1 << 14) /* * splice flags */ #define PPM_SPLICE_F_MOVE (1 << 0) #define PPM_SPLICE_F_NONBLOCK (1 << 1) #define PPM_SPLICE_F_MORE (1 << 2) #define PPM_SPLICE_F_GIFT (1 << 3) /* * quotactl cmds */ #define PPM_Q_QUOTAON (1 << 0) #define PPM_Q_QUOTAOFF (1 << 1) #define PPM_Q_GETFMT (1 << 2) #define PPM_Q_GETINFO (1 << 3) #define PPM_Q_SETINFO (1 << 4) #define PPM_Q_GETQUOTA (1 << 5) #define PPM_Q_SETQUOTA (1 << 6) #define PPM_Q_SYNC (1 << 7) #define PPM_Q_XQUOTAON (1 << 8) #define PPM_Q_XQUOTAOFF (1 << 9) #define PPM_Q_XGETQUOTA (1 << 10) #define PPM_Q_XSETQLIM (1 << 11) #define PPM_Q_XGETQSTAT (1 << 12) #define PPM_Q_XQUOTARM (1 << 13) #define PPM_Q_XQUOTASYNC (1 << 14) #define PPM_Q_XGETQSTATV (1 << 15) /* * quotactl types */ #define PPM_USRQUOTA (1 << 0) #define PPM_GRPQUOTA (1 << 1) /* * quotactl dqi_flags */ #define PPM_DQF_NONE (1 << 0) #define PPM_V1_DQF_RSQUASH (1 << 1) /* * quotactl quotafmts */ #define PPM_QFMT_NOT_USED (1 << 0) #define PPM_QFMT_VFS_OLD (1 << 1) #define PPM_QFMT_VFS_V0 (1 << 2) #define PPM_QFMT_VFS_V1 (1 << 3) /* * Semop flags */ #define PPM_IPC_NOWAIT (1 << 0) #define PPM_SEM_UNDO (1 << 1) /* * Semget flags */ #define PPM_IPC_CREAT (1 << 13) #define PPM_IPC_EXCL (1 << 14) #define PPM_IPC_STAT (1 << 0) #define PPM_IPC_SET (1 << 1) #define PPM_IPC_RMID (1 << 2) #define PPM_IPC_INFO (1 << 3) #define PPM_SEM_INFO (1 << 4) #define PPM_SEM_STAT (1 << 5) #define PPM_GETALL (1 << 6) #define PPM_GETNCNT (1 << 7) #define PPM_GETPID (1 << 8) #define PPM_GETVAL (1 << 9) #define PPM_GETZCNT (1 << 10) #define PPM_SETALL (1 << 11) #define PPM_SETVAL (1 << 12) /* * Access flags */ #define PPM_F_OK (0) #define PPM_X_OK (1 << 0) #define PPM_W_OK (1 << 1) #define PPM_R_OK (1 << 2) /* * Page fault flags */ #define PPM_PF_PROTECTION_VIOLATION (1 << 0) #define PPM_PF_PAGE_NOT_PRESENT (1 << 1) #define PPM_PF_WRITE_ACCESS (1 << 2) #define PPM_PF_READ_ACCESS (1 << 3) #define PPM_PF_USER_FAULT (1 << 4) #define PPM_PF_SUPERVISOR_FAULT (1 << 5) #define PPM_PF_RESERVED_PAGE (1 << 6) #define PPM_PF_INSTRUCTION_FETCH (1 << 7) /* * SuS says limits have to be unsigned. * Which makes a ton more sense anyway. * * Some architectures override this (for compatibility reasons): */ #ifndef RLIM_INFINITY # define RLIM_INFINITY (~0UL) #endif /* * RLIMIT_STACK default maximum - some architectures override it: */ #ifndef _STK_LIM_MAX # define _STK_LIM_MAX RLIM_INFINITY #endif /* * The list of event types * Enter events have even numbers while exit events have odd numbers. * NOTE: there can't be gaps in the numbering, because these numbers correspond * to the entries in the g_event_info table */ #define PPME_DIRECTION_FLAG 1 #define PPME_IS_ENTER(x) ((x & PPME_DIRECTION_FLAG) == 0) #define PPME_IS_EXIT(x) (x & PPME_DIRECTION_FLAG) #define PPME_MAKE_ENTER(x) (x & (~1)) /* * Event category to classify events in generic categories */ enum ppm_capture_category { PPMC_NONE = 0, PPMC_SYSCALL = 1, PPMC_CONTEXT_SWITCH = 2, PPMC_SIGNAL = 3, PPMC_PAGE_FAULT = 4, }; /** @defgroup etypes Event Types * @{ */ enum ppm_event_type { PPME_GENERIC_E = 0, PPME_GENERIC_X = 1, PPME_SYSCALL_OPEN_E = 2, PPME_SYSCALL_OPEN_X = 3, PPME_SYSCALL_CLOSE_E = 4, PPME_SYSCALL_CLOSE_X = 5, PPME_SYSCALL_READ_E = 6, PPME_SYSCALL_READ_X = 7, PPME_SYSCALL_WRITE_E = 8, PPME_SYSCALL_WRITE_X = 9, PPME_SYSCALL_BRK_1_E = 10, PPME_SYSCALL_BRK_1_X = 11, PPME_SYSCALL_EXECVE_8_E = 12, PPME_SYSCALL_EXECVE_8_X = 13, PPME_SYSCALL_CLONE_11_E = 14, PPME_SYSCALL_CLONE_11_X = 15, PPME_PROCEXIT_E = 16, PPME_PROCEXIT_X = 17, /* This should never be called */ PPME_SOCKET_SOCKET_E = 18, PPME_SOCKET_SOCKET_X = 19, PPME_SOCKET_BIND_E = 20, PPME_SOCKET_BIND_X = 21, PPME_SOCKET_CONNECT_E = 22, PPME_SOCKET_CONNECT_X = 23, PPME_SOCKET_LISTEN_E = 24, PPME_SOCKET_LISTEN_X = 25, PPME_SOCKET_ACCEPT_E = 26, PPME_SOCKET_ACCEPT_X = 27, PPME_SOCKET_SEND_E = 28, PPME_SOCKET_SEND_X = 29, PPME_SOCKET_SENDTO_E = 30, PPME_SOCKET_SENDTO_X = 31, PPME_SOCKET_RECV_E = 32, PPME_SOCKET_RECV_X = 33, PPME_SOCKET_RECVFROM_E = 34, PPME_SOCKET_RECVFROM_X = 35, PPME_SOCKET_SHUTDOWN_E = 36, PPME_SOCKET_SHUTDOWN_X = 37, PPME_SOCKET_GETSOCKNAME_E = 38, PPME_SOCKET_GETSOCKNAME_X = 39, PPME_SOCKET_GETPEERNAME_E = 40, PPME_SOCKET_GETPEERNAME_X = 41, PPME_SOCKET_SOCKETPAIR_E = 42, PPME_SOCKET_SOCKETPAIR_X = 43, PPME_SOCKET_SETSOCKOPT_E = 44, PPME_SOCKET_SETSOCKOPT_X = 45, PPME_SOCKET_GETSOCKOPT_E = 46, PPME_SOCKET_GETSOCKOPT_X = 47, PPME_SOCKET_SENDMSG_E = 48, PPME_SOCKET_SENDMSG_X = 49, PPME_SOCKET_SENDMMSG_E = 50, PPME_SOCKET_SENDMMSG_X = 51, PPME_SOCKET_RECVMSG_E = 52, PPME_SOCKET_RECVMSG_X = 53, PPME_SOCKET_RECVMMSG_E = 54, PPME_SOCKET_RECVMMSG_X = 55, PPME_SOCKET_ACCEPT4_E = 56, PPME_SOCKET_ACCEPT4_X = 57, PPME_SYSCALL_CREAT_E = 58, PPME_SYSCALL_CREAT_X = 59, PPME_SYSCALL_PIPE_E = 60, PPME_SYSCALL_PIPE_X = 61, PPME_SYSCALL_EVENTFD_E = 62, PPME_SYSCALL_EVENTFD_X = 63, PPME_SYSCALL_FUTEX_E = 64, PPME_SYSCALL_FUTEX_X = 65, PPME_SYSCALL_STAT_E = 66, PPME_SYSCALL_STAT_X = 67, PPME_SYSCALL_LSTAT_E = 68, PPME_SYSCALL_LSTAT_X = 69, PPME_SYSCALL_FSTAT_E = 70, PPME_SYSCALL_FSTAT_X = 71, PPME_SYSCALL_STAT64_E = 72, PPME_SYSCALL_STAT64_X = 73, PPME_SYSCALL_LSTAT64_E = 74, PPME_SYSCALL_LSTAT64_X = 75, PPME_SYSCALL_FSTAT64_E = 76, PPME_SYSCALL_FSTAT64_X = 77, PPME_SYSCALL_EPOLLWAIT_E = 78, PPME_SYSCALL_EPOLLWAIT_X = 79, PPME_SYSCALL_POLL_E = 80, PPME_SYSCALL_POLL_X = 81, PPME_SYSCALL_SELECT_E = 82, PPME_SYSCALL_SELECT_X = 83, PPME_SYSCALL_NEWSELECT_E = 84, PPME_SYSCALL_NEWSELECT_X = 85, PPME_SYSCALL_LSEEK_E = 86, PPME_SYSCALL_LSEEK_X = 87, PPME_SYSCALL_LLSEEK_E = 88, PPME_SYSCALL_LLSEEK_X = 89, PPME_SYSCALL_IOCTL_2_E = 90, PPME_SYSCALL_IOCTL_2_X = 91, PPME_SYSCALL_GETCWD_E = 92, PPME_SYSCALL_GETCWD_X = 93, PPME_SYSCALL_CHDIR_E = 94, PPME_SYSCALL_CHDIR_X = 95, PPME_SYSCALL_FCHDIR_E = 96, PPME_SYSCALL_FCHDIR_X = 97, /* mkdir/rmdir events are not emitted anymore */ PPME_SYSCALL_MKDIR_E = 98, PPME_SYSCALL_MKDIR_X = 99, PPME_SYSCALL_RMDIR_E = 100, PPME_SYSCALL_RMDIR_X = 101, PPME_SYSCALL_OPENAT_E = 102, PPME_SYSCALL_OPENAT_X = 103, PPME_SYSCALL_LINK_E = 104, PPME_SYSCALL_LINK_X = 105, PPME_SYSCALL_LINKAT_E = 106, PPME_SYSCALL_LINKAT_X = 107, PPME_SYSCALL_UNLINK_E = 108, PPME_SYSCALL_UNLINK_X = 109, PPME_SYSCALL_UNLINKAT_E = 110, PPME_SYSCALL_UNLINKAT_X = 111, PPME_SYSCALL_PREAD_E = 112, PPME_SYSCALL_PREAD_X = 113, PPME_SYSCALL_PWRITE_E = 114, PPME_SYSCALL_PWRITE_X = 115, PPME_SYSCALL_READV_E = 116, PPME_SYSCALL_READV_X = 117, PPME_SYSCALL_WRITEV_E = 118, PPME_SYSCALL_WRITEV_X = 119, PPME_SYSCALL_PREADV_E = 120, PPME_SYSCALL_PREADV_X = 121, PPME_SYSCALL_PWRITEV_E = 122, PPME_SYSCALL_PWRITEV_X = 123, PPME_SYSCALL_DUP_E = 124, PPME_SYSCALL_DUP_X = 125, PPME_SYSCALL_SIGNALFD_E = 126, PPME_SYSCALL_SIGNALFD_X = 127, PPME_SYSCALL_KILL_E = 128, PPME_SYSCALL_KILL_X = 129, PPME_SYSCALL_TKILL_E = 130, PPME_SYSCALL_TKILL_X = 131, PPME_SYSCALL_TGKILL_E = 132, PPME_SYSCALL_TGKILL_X = 133, PPME_SYSCALL_NANOSLEEP_E = 134, PPME_SYSCALL_NANOSLEEP_X = 135, PPME_SYSCALL_TIMERFD_CREATE_E = 136, PPME_SYSCALL_TIMERFD_CREATE_X = 137, PPME_SYSCALL_INOTIFY_INIT_E = 138, PPME_SYSCALL_INOTIFY_INIT_X = 139, PPME_SYSCALL_GETRLIMIT_E = 140, PPME_SYSCALL_GETRLIMIT_X = 141, PPME_SYSCALL_SETRLIMIT_E = 142, PPME_SYSCALL_SETRLIMIT_X = 143, PPME_SYSCALL_PRLIMIT_E = 144, PPME_SYSCALL_PRLIMIT_X = 145, PPME_SCHEDSWITCH_1_E = 146, PPME_SCHEDSWITCH_1_X = 147, /* This should never be called */ PPME_DROP_E = 148, /* For internal use */ PPME_DROP_X = 149, /* For internal use */ PPME_SYSCALL_FCNTL_E = 150, /* For internal use */ PPME_SYSCALL_FCNTL_X = 151, /* For internal use */ PPME_SCHEDSWITCH_6_E = 152, PPME_SCHEDSWITCH_6_X = 153, /* This should never be called */ PPME_SYSCALL_EXECVE_13_E = 154, PPME_SYSCALL_EXECVE_13_X = 155, PPME_SYSCALL_CLONE_16_E = 156, PPME_SYSCALL_CLONE_16_X = 157, PPME_SYSCALL_BRK_4_E = 158, PPME_SYSCALL_BRK_4_X = 159, PPME_SYSCALL_MMAP_E = 160, PPME_SYSCALL_MMAP_X = 161, PPME_SYSCALL_MMAP2_E = 162, PPME_SYSCALL_MMAP2_X = 163, PPME_SYSCALL_MUNMAP_E = 164, PPME_SYSCALL_MUNMAP_X = 165, PPME_SYSCALL_SPLICE_E = 166, PPME_SYSCALL_SPLICE_X = 167, PPME_SYSCALL_PTRACE_E = 168, PPME_SYSCALL_PTRACE_X = 169, PPME_SYSCALL_IOCTL_3_E = 170, PPME_SYSCALL_IOCTL_3_X = 171, PPME_SYSCALL_EXECVE_14_E = 172, PPME_SYSCALL_EXECVE_14_X = 173, PPME_SYSCALL_RENAME_E = 174, PPME_SYSCALL_RENAME_X = 175, PPME_SYSCALL_RENAMEAT_E = 176, PPME_SYSCALL_RENAMEAT_X = 177, PPME_SYSCALL_SYMLINK_E = 178, PPME_SYSCALL_SYMLINK_X = 179, PPME_SYSCALL_SYMLINKAT_E = 180, PPME_SYSCALL_SYMLINKAT_X = 181, PPME_SYSCALL_FORK_E = 182, PPME_SYSCALL_FORK_X = 183, PPME_SYSCALL_VFORK_E = 184, PPME_SYSCALL_VFORK_X = 185, PPME_PROCEXIT_1_E = 186, PPME_PROCEXIT_1_X = 187, /* This should never be called */ PPME_SYSCALL_SENDFILE_E = 188, PPME_SYSCALL_SENDFILE_X = 189, /* This should never be called */ PPME_SYSCALL_QUOTACTL_E = 190, PPME_SYSCALL_QUOTACTL_X = 191, PPME_SYSCALL_SETRESUID_E = 192, PPME_SYSCALL_SETRESUID_X = 193, PPME_SYSCALL_SETRESGID_E = 194, PPME_SYSCALL_SETRESGID_X = 195, PPME_SYSDIGEVENT_E = 196, PPME_SYSDIGEVENT_X = 197, /* This should never be called */ PPME_SYSCALL_SETUID_E = 198, PPME_SYSCALL_SETUID_X = 199, PPME_SYSCALL_SETGID_E = 200, PPME_SYSCALL_SETGID_X = 201, PPME_SYSCALL_GETUID_E = 202, PPME_SYSCALL_GETUID_X = 203, PPME_SYSCALL_GETEUID_E = 204, PPME_SYSCALL_GETEUID_X = 205, PPME_SYSCALL_GETGID_E = 206, PPME_SYSCALL_GETGID_X = 207, PPME_SYSCALL_GETEGID_E = 208, PPME_SYSCALL_GETEGID_X = 209, PPME_SYSCALL_GETRESUID_E = 210, PPME_SYSCALL_GETRESUID_X = 211, PPME_SYSCALL_GETRESGID_E = 212, PPME_SYSCALL_GETRESGID_X = 213, PPME_SYSCALL_EXECVE_15_E = 214, PPME_SYSCALL_EXECVE_15_X = 215, PPME_SYSCALL_CLONE_17_E = 216, PPME_SYSCALL_CLONE_17_X = 217, PPME_SYSCALL_FORK_17_E = 218, PPME_SYSCALL_FORK_17_X = 219, PPME_SYSCALL_VFORK_17_E = 220, PPME_SYSCALL_VFORK_17_X = 221, PPME_SYSCALL_CLONE_20_E = 222, PPME_SYSCALL_CLONE_20_X = 223, PPME_SYSCALL_FORK_20_E = 224, PPME_SYSCALL_FORK_20_X = 225, PPME_SYSCALL_VFORK_20_E = 226, PPME_SYSCALL_VFORK_20_X = 227, PPME_CONTAINER_E = 228, PPME_CONTAINER_X = 229, PPME_SYSCALL_EXECVE_16_E = 230, PPME_SYSCALL_EXECVE_16_X = 231, PPME_SIGNALDELIVER_E = 232, PPME_SIGNALDELIVER_X = 233, /* This should never be called */ PPME_PROCINFO_E = 234, PPME_PROCINFO_X = 235, /* This should never be called */ PPME_SYSCALL_GETDENTS_E = 236, PPME_SYSCALL_GETDENTS_X = 237, PPME_SYSCALL_GETDENTS64_E = 238, PPME_SYSCALL_GETDENTS64_X = 239, PPME_SYSCALL_SETNS_E = 240, PPME_SYSCALL_SETNS_X = 241, PPME_SYSCALL_FLOCK_E = 242, PPME_SYSCALL_FLOCK_X = 243, PPME_CPU_HOTPLUG_E = 244, PPME_CPU_HOTPLUG_X = 245, /* This should never be called */ PPME_SOCKET_ACCEPT_5_E = 246, PPME_SOCKET_ACCEPT_5_X = 247, PPME_SOCKET_ACCEPT4_5_E = 248, PPME_SOCKET_ACCEPT4_5_X = 249, PPME_SYSCALL_SEMOP_E = 250, PPME_SYSCALL_SEMOP_X = 251, PPME_SYSCALL_SEMCTL_E = 252, PPME_SYSCALL_SEMCTL_X = 253, PPME_SYSCALL_PPOLL_E = 254, PPME_SYSCALL_PPOLL_X = 255, PPME_SYSCALL_MOUNT_E = 256, PPME_SYSCALL_MOUNT_X = 257, PPME_SYSCALL_UMOUNT_E = 258, PPME_SYSCALL_UMOUNT_X = 259, PPME_K8S_E = 260, PPME_K8S_X = 261, PPME_SYSCALL_SEMGET_E = 262, PPME_SYSCALL_SEMGET_X = 263, PPME_SYSCALL_ACCESS_E = 264, PPME_SYSCALL_ACCESS_X = 265, PPME_SYSCALL_CHROOT_E = 266, PPME_SYSCALL_CHROOT_X = 267, PPME_TRACER_E = 268, PPME_TRACER_X = 269, PPME_MESOS_E = 270, PPME_MESOS_X = 271, PPME_CONTAINER_JSON_E = 272, PPME_CONTAINER_JSON_X = 273, PPME_SYSCALL_SETSID_E = 274, PPME_SYSCALL_SETSID_X = 275, PPME_SYSCALL_MKDIR_2_E = 276, PPME_SYSCALL_MKDIR_2_X = 277, PPME_SYSCALL_RMDIR_2_E = 278, PPME_SYSCALL_RMDIR_2_X = 279, PPME_NOTIFICATION_E = 280, PPME_NOTIFICATION_X = 281, PPME_SYSCALL_EXECVE_17_E = 282, PPME_SYSCALL_EXECVE_17_X = 283, PPME_SYSCALL_UNSHARE_E = 284, PPME_SYSCALL_UNSHARE_X = 285, PPME_INFRASTRUCTURE_EVENT_E = 286, PPME_INFRASTRUCTURE_EVENT_X = 287, PPME_SYSCALL_EXECVE_18_E = 288, PPME_SYSCALL_EXECVE_18_X = 289, PPME_PAGE_FAULT_E = 290, PPME_PAGE_FAULT_X = 291, PPME_SYSCALL_EXECVE_19_E = 292, PPME_SYSCALL_EXECVE_19_X = 293, PPME_SYSCALL_SETPGID_E = 294, PPME_SYSCALL_SETPGID_X = 295, PPME_SYSCALL_BPF_E = 296, PPME_SYSCALL_BPF_X = 297, PPME_SYSCALL_SECCOMP_E = 298, PPME_SYSCALL_SECCOMP_X = 299, PPME_SYSCALL_UNLINK_2_E = 300, PPME_SYSCALL_UNLINK_2_X = 301, PPME_SYSCALL_UNLINKAT_2_E = 302, PPME_SYSCALL_UNLINKAT_2_X = 303, PPME_SYSCALL_MKDIRAT_E = 304, PPME_SYSCALL_MKDIRAT_X = 305, PPME_SYSCALL_OPENAT_2_E = 306, PPME_SYSCALL_OPENAT_2_X = 307, PPME_SYSCALL_LINK_2_E = 308, PPME_SYSCALL_LINK_2_X = 309, PPME_SYSCALL_LINKAT_2_E = 310, PPME_SYSCALL_LINKAT_2_X = 311, PPME_SYSCALL_FCHMODAT_E = 312, PPME_SYSCALL_FCHMODAT_X = 313, PPME_SYSCALL_CHMOD_E = 314, PPME_SYSCALL_CHMOD_X = 315, PPME_SYSCALL_FCHMOD_E = 316, PPME_SYSCALL_FCHMOD_X = 317, PPM_EVENT_MAX = 318 }; /*@}*/ /* * System-independent syscall codes */ enum ppm_syscall_code { PPM_SC_UNKNOWN = 0, PPM_SC_RESTART_SYSCALL = 1, PPM_SC_EXIT = 2, PPM_SC_READ = 3, PPM_SC_WRITE = 4, PPM_SC_OPEN = 5, PPM_SC_CLOSE = 6, PPM_SC_CREAT = 7, PPM_SC_LINK = 8, PPM_SC_UNLINK = 9, PPM_SC_CHDIR = 10, PPM_SC_TIME = 11, PPM_SC_MKNOD = 12, PPM_SC_CHMOD = 13, PPM_SC_STAT = 14, PPM_SC_LSEEK = 15, PPM_SC_GETPID = 16, PPM_SC_MOUNT = 17, PPM_SC_PTRACE = 18, PPM_SC_ALARM = 19, PPM_SC_FSTAT = 20, PPM_SC_PAUSE = 21, PPM_SC_UTIME = 22, PPM_SC_ACCESS = 23, PPM_SC_SYNC = 24, PPM_SC_KILL = 25, PPM_SC_RENAME = 26, PPM_SC_MKDIR = 27, PPM_SC_RMDIR = 28, PPM_SC_DUP = 29, PPM_SC_PIPE = 30, PPM_SC_TIMES = 31, PPM_SC_BRK = 32, PPM_SC_ACCT = 33, PPM_SC_IOCTL = 34, PPM_SC_FCNTL = 35, PPM_SC_SETPGID = 36, PPM_SC_UMASK = 37, PPM_SC_CHROOT = 38, PPM_SC_USTAT = 39, PPM_SC_DUP2 = 40, PPM_SC_GETPPID = 41, PPM_SC_GETPGRP = 42, PPM_SC_SETSID = 43, PPM_SC_SETHOSTNAME = 44, PPM_SC_SETRLIMIT = 45, PPM_SC_GETRUSAGE = 46, PPM_SC_GETTIMEOFDAY = 47, PPM_SC_SETTIMEOFDAY = 48, PPM_SC_SYMLINK = 49, PPM_SC_LSTAT = 50, PPM_SC_READLINK = 51, PPM_SC_USELIB = 52, PPM_SC_SWAPON = 53, PPM_SC_REBOOT = 54, PPM_SC_MMAP = 55, PPM_SC_MUNMAP = 56, PPM_SC_TRUNCATE = 57, PPM_SC_FTRUNCATE = 58, PPM_SC_FCHMOD = 59, PPM_SC_GETPRIORITY = 60, PPM_SC_SETPRIORITY = 61, PPM_SC_STATFS = 62, PPM_SC_FSTATFS = 63, PPM_SC_SYSLOG = 64, PPM_SC_SETITIMER = 65, PPM_SC_GETITIMER = 66, PPM_SC_UNAME = 67, PPM_SC_VHANGUP = 68, PPM_SC_WAIT4 = 69, PPM_SC_SWAPOFF = 70, PPM_SC_SYSINFO = 71, PPM_SC_FSYNC = 72, PPM_SC_SETDOMAINNAME = 73, PPM_SC_ADJTIMEX = 74, PPM_SC_MPROTECT = 75, PPM_SC_INIT_MODULE = 76, PPM_SC_DELETE_MODULE = 77, PPM_SC_QUOTACTL = 78, PPM_SC_GETPGID = 79, PPM_SC_FCHDIR = 80, PPM_SC_SYSFS = 81, PPM_SC_PERSONALITY = 82, PPM_SC_GETDENTS = 83, PPM_SC_SELECT = 84, PPM_SC_FLOCK = 85, PPM_SC_MSYNC = 86, PPM_SC_READV = 87, PPM_SC_WRITEV = 88, PPM_SC_GETSID = 89, PPM_SC_FDATASYNC = 90, PPM_SC_MLOCK = 91, PPM_SC_MUNLOCK = 92, PPM_SC_MLOCKALL = 93, PPM_SC_MUNLOCKALL = 94, PPM_SC_SCHED_SETPARAM = 95, PPM_SC_SCHED_GETPARAM = 96, PPM_SC_SCHED_SETSCHEDULER = 97, PPM_SC_SCHED_GETSCHEDULER = 98, PPM_SC_SCHED_YIELD = 99, PPM_SC_SCHED_GET_PRIORITY_MAX = 100, PPM_SC_SCHED_GET_PRIORITY_MIN = 101, PPM_SC_SCHED_RR_GET_INTERVAL = 102, PPM_SC_NANOSLEEP = 103, PPM_SC_MREMAP = 104, PPM_SC_POLL = 105, PPM_SC_PRCTL = 106, PPM_SC_RT_SIGACTION = 107, PPM_SC_RT_SIGPROCMASK = 108, PPM_SC_RT_SIGPENDING = 109, PPM_SC_RT_SIGTIMEDWAIT = 110, PPM_SC_RT_SIGQUEUEINFO = 111, PPM_SC_RT_SIGSUSPEND = 112, PPM_SC_GETCWD = 113, PPM_SC_CAPGET = 114, PPM_SC_CAPSET = 115, PPM_SC_SENDFILE = 116, PPM_SC_GETRLIMIT = 117, PPM_SC_LCHOWN = 118, PPM_SC_GETUID = 119, PPM_SC_GETGID = 120, PPM_SC_GETEUID = 121, PPM_SC_GETEGID = 122, PPM_SC_SETREUID = 123, PPM_SC_SETREGID = 124, PPM_SC_GETGROUPS = 125, PPM_SC_SETGROUPS = 126, PPM_SC_FCHOWN = 127, PPM_SC_SETRESUID = 128, PPM_SC_GETRESUID = 129, PPM_SC_SETRESGID = 130, PPM_SC_GETRESGID = 131, PPM_SC_CHOWN = 132, PPM_SC_SETUID = 133, PPM_SC_SETGID = 134, PPM_SC_SETFSUID = 135, PPM_SC_SETFSGID = 136, PPM_SC_PIVOT_ROOT = 137, PPM_SC_MINCORE = 138, PPM_SC_MADVISE = 139, PPM_SC_GETTID = 140, PPM_SC_SETXATTR = 141, PPM_SC_LSETXATTR = 142, PPM_SC_FSETXATTR = 143, PPM_SC_GETXATTR = 144, PPM_SC_LGETXATTR = 145, PPM_SC_FGETXATTR = 146, PPM_SC_LISTXATTR = 147, PPM_SC_LLISTXATTR = 148, PPM_SC_FLISTXATTR = 149, PPM_SC_REMOVEXATTR = 150, PPM_SC_LREMOVEXATTR = 151, PPM_SC_FREMOVEXATTR = 152, PPM_SC_TKILL = 153, PPM_SC_FUTEX = 154, PPM_SC_SCHED_SETAFFINITY = 155, PPM_SC_SCHED_GETAFFINITY = 156, PPM_SC_SET_THREAD_AREA = 157, PPM_SC_GET_THREAD_AREA = 158, PPM_SC_IO_SETUP = 159, PPM_SC_IO_DESTROY = 160, PPM_SC_IO_GETEVENTS = 161, PPM_SC_IO_SUBMIT = 162, PPM_SC_IO_CANCEL = 163, PPM_SC_EXIT_GROUP = 164, PPM_SC_EPOLL_CREATE = 165, PPM_SC_EPOLL_CTL = 166, PPM_SC_EPOLL_WAIT = 167, PPM_SC_REMAP_FILE_PAGES = 168, PPM_SC_SET_TID_ADDRESS = 169, PPM_SC_TIMER_CREATE = 170, PPM_SC_TIMER_SETTIME = 171, PPM_SC_TIMER_GETTIME = 172, PPM_SC_TIMER_GETOVERRUN = 173, PPM_SC_TIMER_DELETE = 174, PPM_SC_CLOCK_SETTIME = 175, PPM_SC_CLOCK_GETTIME = 176, PPM_SC_CLOCK_GETRES = 177, PPM_SC_CLOCK_NANOSLEEP = 178, PPM_SC_TGKILL = 179, PPM_SC_UTIMES = 180, PPM_SC_MQ_OPEN = 181, PPM_SC_MQ_UNLINK = 182, PPM_SC_MQ_TIMEDSEND = 183, PPM_SC_MQ_TIMEDRECEIVE = 184, PPM_SC_MQ_NOTIFY = 185, PPM_SC_MQ_GETSETATTR = 186, PPM_SC_KEXEC_LOAD = 187, PPM_SC_WAITID = 188, PPM_SC_ADD_KEY = 189, PPM_SC_REQUEST_KEY = 190, PPM_SC_KEYCTL = 191, PPM_SC_IOPRIO_SET = 192, PPM_SC_IOPRIO_GET = 193, PPM_SC_INOTIFY_INIT = 194, PPM_SC_INOTIFY_ADD_WATCH = 195, PPM_SC_INOTIFY_RM_WATCH = 196, PPM_SC_OPENAT = 197, PPM_SC_MKDIRAT = 198, PPM_SC_MKNODAT = 199, PPM_SC_FCHOWNAT = 200, PPM_SC_FUTIMESAT = 201, PPM_SC_UNLINKAT = 202, PPM_SC_RENAMEAT = 203, PPM_SC_LINKAT = 204, PPM_SC_SYMLINKAT = 205, PPM_SC_READLINKAT = 206, PPM_SC_FCHMODAT = 207, PPM_SC_FACCESSAT = 208, PPM_SC_PSELECT6 = 209, PPM_SC_PPOLL = 210, PPM_SC_UNSHARE = 211, PPM_SC_SET_ROBUST_LIST = 212, PPM_SC_GET_ROBUST_LIST = 213, PPM_SC_SPLICE = 214, PPM_SC_TEE = 215, PPM_SC_VMSPLICE = 216, PPM_SC_GETCPU = 217, PPM_SC_EPOLL_PWAIT = 218, PPM_SC_UTIMENSAT = 219, PPM_SC_SIGNALFD = 220, PPM_SC_TIMERFD_CREATE = 221, PPM_SC_EVENTFD = 222, PPM_SC_TIMERFD_SETTIME = 223, PPM_SC_TIMERFD_GETTIME = 224, PPM_SC_SIGNALFD4 = 225, PPM_SC_EVENTFD2 = 226, PPM_SC_EPOLL_CREATE1 = 227, PPM_SC_DUP3 = 228, PPM_SC_PIPE2 = 229, PPM_SC_INOTIFY_INIT1 = 230, PPM_SC_PREADV = 231, PPM_SC_PWRITEV = 232, PPM_SC_RT_TGSIGQUEUEINFO = 233, PPM_SC_PERF_EVENT_OPEN = 234, PPM_SC_FANOTIFY_INIT = 235, PPM_SC_PRLIMIT64 = 236, PPM_SC_CLOCK_ADJTIME = 237, PPM_SC_SYNCFS = 238, PPM_SC_SETNS = 239, PPM_SC_GETDENTS64 = 240, PPM_SC_SOCKET = 241, PPM_SC_BIND = 242, PPM_SC_CONNECT = 243, PPM_SC_LISTEN = 244, PPM_SC_ACCEPT = 245, PPM_SC_GETSOCKNAME = 246, PPM_SC_GETPEERNAME = 247, PPM_SC_SOCKETPAIR = 248, PPM_SC_SENDTO = 249, PPM_SC_RECVFROM = 250, PPM_SC_SHUTDOWN = 251, PPM_SC_SETSOCKOPT = 252, PPM_SC_GETSOCKOPT = 253, PPM_SC_SENDMSG = 254, PPM_SC_SENDMMSG = 255, PPM_SC_RECVMSG = 256, PPM_SC_RECVMMSG = 257, PPM_SC_ACCEPT4 = 258, PPM_SC_SEMOP = 259, PPM_SC_SEMGET = 260, PPM_SC_SEMCTL = 261, PPM_SC_MSGSND = 262, PPM_SC_MSGRCV = 263, PPM_SC_MSGGET = 264, PPM_SC_MSGCTL = 265, PPM_SC_SHMDT = 266, PPM_SC_SHMGET = 267, PPM_SC_SHMCTL = 268, PPM_SC_STATFS64 = 269, PPM_SC_FSTATFS64 = 270, PPM_SC_FSTATAT64 = 271, PPM_SC_SENDFILE64 = 272, PPM_SC_UGETRLIMIT = 273, PPM_SC_BDFLUSH = 274, PPM_SC_SIGPROCMASK = 275, PPM_SC_IPC = 276, PPM_SC_SOCKETCALL = 277, PPM_SC_STAT64 = 278, PPM_SC_LSTAT64 = 279, PPM_SC_FSTAT64 = 280, PPM_SC_FCNTL64 = 281, PPM_SC_MMAP2 = 282, PPM_SC__NEWSELECT = 283, PPM_SC_SGETMASK = 284, PPM_SC_SSETMASK = 285, PPM_SC_SIGPENDING = 286, PPM_SC_OLDUNAME = 287, PPM_SC_UMOUNT = 288, PPM_SC_SIGNAL = 289, PPM_SC_NICE = 290, PPM_SC_STIME = 291, PPM_SC__LLSEEK = 292, PPM_SC_WAITPID = 293, PPM_SC_PREAD64 = 294, PPM_SC_PWRITE64 = 295, PPM_SC_ARCH_PRCTL = 296, PPM_SC_SHMAT = 297, PPM_SC_SIGRETURN = 298, PPM_SC_FALLOCATE = 299, PPM_SC_NEWFSSTAT = 300, PPM_SC_PROCESS_VM_READV = 301, PPM_SC_PROCESS_VM_WRITEV = 302, PPM_SC_FORK = 303, PPM_SC_VFORK = 304, PPM_SC_SETUID32 = 305, PPM_SC_GETUID32 = 306, PPM_SC_SETGID32 = 307, PPM_SC_GETEUID32 = 308, PPM_SC_GETGID32 = 309, PPM_SC_SETRESUID32 = 310, PPM_SC_SETRESGID32 = 311, PPM_SC_GETRESUID32 = 312, PPM_SC_GETRESGID32 = 313, PPM_SC_FINIT_MODULE = 314, PPM_SC_BPF = 315, PPM_SC_SECCOMP = 316, PPM_SC_SIGALTSTACK = 317, PPM_SC_GETRANDOM = 318, PPM_SC_FADVISE64 = 319, PPM_SC_MAX = 320, }; /* * Event information enums */ enum ppm_event_category { EC_UNKNOWN = 0, /* Unknown */ EC_OTHER = 1, /* No specific category */ EC_FILE = 2, /* File operation (open, close...) or file I/O */ EC_NET = 3, /* Network operation (socket, bind...) or network I/O */ EC_IPC = 4, /* IPC operation (pipe, futex...) or IPC I/O (e.g. on a pipe) */ EC_MEMORY = 5, /* Memory-related operation (e.g. brk) */ EC_PROCESS = 6, /* Process-related operation (fork, clone...) */ EC_SLEEP = 7, /* Plain sleep */ EC_SYSTEM = 8, /* System-related operations (e.g. reboot) */ EC_SIGNAL = 9, /* Signal-related operations (e.g. signal) */ EC_USER = 10, /* User-related operations (e.g. getuid) */ EC_TIME = 11, /* Time-related syscalls (e.g. gettimeofday) */ EC_PROCESSING = 12, /* User level processing. Never used for system calls */ EC_IO_BASE = 32,/* used for masking */ EC_IO_READ = 32,/* General I/O read (can be file, socket, IPC...) */ EC_IO_WRITE = 33,/* General I/O write (can be file, socket, IPC...) */ EC_IO_OTHER = 34,/* General I/O that is neither read not write (can be file, socket, IPC...) */ EC_WAIT = 64, /* General wait (can be file, socket, IPC...) */ EC_SCHEDULER = 128, /* Scheduler event (e.g. context switch) */ EC_INTERNAL = 256, /* Internal event that shouldn't be shown to the user */ }; enum ppm_event_flags { EF_NONE = 0, EF_CREATES_FD = (1 << 0), /* This event creates an FD (e.g. open) */ EF_DESTROYS_FD = (1 << 1), /* This event destroys an FD (e.g. close) */ EF_USES_FD = (1 << 2), /* This event operates on an FD. */ EF_READS_FROM_FD = (1 << 3), /* This event reads data from an FD. */ EF_WRITES_TO_FD = (1 << 4), /* This event writes data to an FD. */ EF_MODIFIES_STATE = (1 << 5), /* This event causes the machine state to change and should not be dropped by the filtering engine. */ EF_UNUSED = (1 << 6), /* This event is not used */ EF_WAITS = (1 << 7), /* This event reads data from an FD. */ EF_SKIPPARSERESET = (1 << 8), /* This event shouldn't pollute the parser lastevent state tracker. */ EF_OLD_VERSION = (1 << 9), /* This event is kept for backward compatibility */ EF_DROP_FALCO = (1 << 10) /* This event should not be passed up to Falco */ }; /* * types of event parameters */ enum ppm_param_type { PT_NONE = 0, PT_INT8 = 1, PT_INT16 = 2, PT_INT32 = 3, PT_INT64 = 4, PT_UINT8 = 5, PT_UINT16 = 6, PT_UINT32 = 7, PT_UINT64 = 8, PT_CHARBUF = 9, /* A printable buffer of bytes, NULL terminated */ PT_BYTEBUF = 10, /* A raw buffer of bytes not suitable for printing */ PT_ERRNO = 11, /* this is an INT64, but will be interpreted as an error code */ PT_SOCKADDR = 12, /* A sockaddr structure, 1byte family + data */ PT_SOCKTUPLE = 13, /* A sockaddr tuple,1byte family + 12byte data + 12byte data */ PT_FD = 14, /* An fd, 64bit */ PT_PID = 15, /* A pid/tid, 64bit */ PT_FDLIST = 16, /* A list of fds, 16bit count + count * (64bit fd + 16bit flags) */ PT_FSPATH = 17, /* A string containing a relative or absolute file system path, null terminated */ PT_SYSCALLID = 18, /* A 16bit system call ID. Can be used as a key for the g_syscall_info_table table. */ PT_SIGTYPE = 19, /* An 8bit signal number */ PT_RELTIME = 20, /* A relative time. Seconds * 10^9 + nanoseconds. 64bit. */ PT_ABSTIME = 21, /* An absolute time interval. Seconds from epoch * 10^9 + nanoseconds. 64bit. */ PT_PORT = 22, /* A TCP/UDP prt. 2 bytes. */ PT_L4PROTO = 23, /* A 1 byte IP protocol type. */ PT_SOCKFAMILY = 24, /* A 1 byte socket family. */ PT_BOOL = 25, /* A boolean value, 4 bytes. */ PT_IPV4ADDR = 26, /* A 4 byte raw IPv4 address. */ PT_DYN = 27, /* Type can vary depending on the context. Used for filter fields like evt.rawarg. */ PT_FLAGS8 = 28, /* this is an UINT8, but will be interpreted as 8 bit flags. */ PT_FLAGS16 = 29, /* this is an UINT16, but will be interpreted as 16 bit flags. */ PT_FLAGS32 = 30, /* this is an UINT32, but will be interpreted as 32 bit flags. */ PT_UID = 31, /* this is an UINT32, MAX_UINT32 will be interpreted as no value. */ PT_GID = 32, /* this is an UINT32, MAX_UINT32 will be interpreted as no value. */ PT_DOUBLE = 33, /* this is a double precision floating point number. */ PT_SIGSET = 34, /* sigset_t. I only store the lower UINT32 of it */ PT_CHARBUFARRAY = 35, /* Pointer to an array of strings, exported by the user events decoder. 64bit. For internal use only. */ PT_CHARBUF_PAIR_ARRAY = 36, /* Pointer to an array of string pairs, exported by the user events decoder. 64bit. For internal use only. */ PT_IPV4NET = 37, /* An IPv4 network. */ PT_IPV6ADDR = 38, /* A 16 byte raw IPv6 address. */ PT_IPV6NET = 39, /* An IPv6 network. */ PT_IPADDR = 40, /* Either an IPv4 or IPv6 address. The length indicates which one it is. */ PT_IPNET = 41, /* Either an IPv4 or IPv6 network. The length indicates which one it is. */ PT_MODE = 42, /* a 32 bit bitmask to represent file modes. */ PT_MAX = 43 /* array size */ }; enum ppm_print_format { PF_NA = 0, PF_DEC = 1, /* decimal */ PF_HEX = 2, /* hexadecimal */ PF_10_PADDED_DEC = 3, /* decimal padded to 10 digits, useful to print the fractional part of a ns timestamp */ PF_ID = 4, PF_DIR = 5, PF_OCT = 6, /* octal */ }; /*! \brief Name-value pair, used to store flags information. */ struct ppm_name_value { const char *name; uint32_t value; }; /*! \brief Event parameter information. */ struct ppm_param_info { char name[PPM_MAX_NAME_LEN]; /**< Parameter name, e.g. 'size'. */ enum ppm_param_type type; /**< Parameter type, e.g. 'uint16', 'string'... */ enum ppm_print_format fmt; /**< If this is a numeric parameter, this flag specifies if it should be rendered as decimal or hex. */ const void *info; /**< If this is a flags parameter, it points to an array of ppm_name_value, else if this is a dynamic parameter it points to an array of ppm_param_info */ uint8_t ninfo; /**< Number of entry in the info array. */ } _packed; /*! \brief Event information. This structure contains the full description of an event type (e.g. 'open') that is supported by the sysdig infrastructure. */ struct ppm_event_info { char name[PPM_MAX_NAME_LEN]; /**< Name. */ enum ppm_event_category category; /**< Event category, e.g. 'file', 'net', etc. */ enum ppm_event_flags flags; /**< flags for this event. */ uint32_t nparams; /**< Number of parameter in the params array. */ struct ppm_param_info params[PPM_MAX_EVENT_PARAMS]; /**< parameters descriptions. */ } _packed; #if defined _MSC_VER #pragma pack(push) #pragma pack(1) #elif defined __sun #pragma pack(1) #else #pragma pack(push, 1) #endif struct ppm_evt_hdr { #ifdef PPM_ENABLE_SENTINEL uint32_t sentinel_begin; #endif uint64_t ts; /* timestamp, in nanoseconds from epoch */ uint64_t tid; /* the tid of the thread that generated this event */ uint32_t len; /* the event len, including the header */ uint16_t type; /* the event type */ uint32_t nparams; /* the number of parameters of the event */ }; #if defined __sun #pragma pack() #else #pragma pack(pop) #endif /* * IOCTL codes */ #ifndef CYGWING_AGENT #define PPM_IOCTL_MAGIC 's' #define PPM_IOCTL_DISABLE_CAPTURE _IO(PPM_IOCTL_MAGIC, 0) #define PPM_IOCTL_ENABLE_CAPTURE _IO(PPM_IOCTL_MAGIC, 1) #define PPM_IOCTL_DISABLE_DROPPING_MODE _IO(PPM_IOCTL_MAGIC, 2) #define PPM_IOCTL_ENABLE_DROPPING_MODE _IO(PPM_IOCTL_MAGIC, 3) #define PPM_IOCTL_SET_SNAPLEN _IO(PPM_IOCTL_MAGIC, 4) #define PPM_IOCTL_MASK_ZERO_EVENTS _IO(PPM_IOCTL_MAGIC, 5) #define PPM_IOCTL_MASK_SET_EVENT _IO(PPM_IOCTL_MAGIC, 6) #define PPM_IOCTL_MASK_UNSET_EVENT _IO(PPM_IOCTL_MAGIC, 7) #define PPM_IOCTL_DISABLE_DYNAMIC_SNAPLEN _IO(PPM_IOCTL_MAGIC, 8) #define PPM_IOCTL_ENABLE_DYNAMIC_SNAPLEN _IO(PPM_IOCTL_MAGIC, 9) #define PPM_IOCTL_GET_VTID _IO(PPM_IOCTL_MAGIC, 10) #define PPM_IOCTL_GET_VPID _IO(PPM_IOCTL_MAGIC, 11) #define PPM_IOCTL_GET_CURRENT_TID _IO(PPM_IOCTL_MAGIC, 12) #define PPM_IOCTL_GET_CURRENT_PID _IO(PPM_IOCTL_MAGIC, 13) #define PPM_IOCTL_DISABLE_SIGNAL_DELIVER _IO(PPM_IOCTL_MAGIC, 14) #define PPM_IOCTL_ENABLE_SIGNAL_DELIVER _IO(PPM_IOCTL_MAGIC, 15) #define PPM_IOCTL_GET_PROCLIST _IO(PPM_IOCTL_MAGIC, 16) #define PPM_IOCTL_SET_TRACERS_CAPTURE _IO(PPM_IOCTL_MAGIC, 17) #define PPM_IOCTL_SET_SIMPLE_MODE _IO(PPM_IOCTL_MAGIC, 18) #define PPM_IOCTL_ENABLE_PAGE_FAULTS _IO(PPM_IOCTL_MAGIC, 19) #define PPM_IOCTL_GET_N_TRACEPOINT_HIT _IO(PPM_IOCTL_MAGIC, 20) #define PPM_IOCTL_GET_PROBE_VERSION _IO(PPM_IOCTL_MAGIC, 21) #define PPM_IOCTL_SET_FULLCAPTURE_PORT_RANGE _IO(PPM_IOCTL_MAGIC, 22) #define PPM_IOCTL_SET_STATSD_PORT _IO(PPM_IOCTL_MAGIC, 23) #endif // CYGWING_AGENT extern const struct ppm_name_value socket_families[]; extern const struct ppm_name_value file_flags[]; extern const struct ppm_name_value flock_flags[]; extern const struct ppm_name_value clone_flags[]; extern const struct ppm_name_value futex_operations[]; extern const struct ppm_name_value lseek_whence[]; extern const struct ppm_name_value poll_flags[]; extern const struct ppm_name_value mount_flags[]; extern const struct ppm_name_value umount_flags[]; extern const struct ppm_name_value shutdown_how[]; extern const struct ppm_name_value rlimit_resources[]; extern const struct ppm_name_value fcntl_commands[]; extern const struct ppm_name_value sockopt_levels[]; extern const struct ppm_name_value sockopt_options[]; extern const struct ppm_name_value ptrace_requests[]; extern const struct ppm_name_value prot_flags[]; extern const struct ppm_name_value mmap_flags[]; extern const struct ppm_name_value splice_flags[]; extern const struct ppm_name_value quotactl_cmds[]; extern const struct ppm_name_value quotactl_types[]; extern const struct ppm_name_value quotactl_dqi_flags[]; extern const struct ppm_name_value quotactl_quota_fmts[]; extern const struct ppm_name_value semop_flags[]; extern const struct ppm_name_value semget_flags[]; extern const struct ppm_name_value semctl_commands[]; extern const struct ppm_name_value access_flags[]; extern const struct ppm_name_value pf_flags[]; extern const struct ppm_name_value unlinkat_flags[]; extern const struct ppm_name_value linkat_flags[]; extern const struct ppm_name_value chmod_mode[]; extern const struct ppm_param_info sockopt_dynamic_param[]; extern const struct ppm_param_info ptrace_dynamic_param[]; extern const struct ppm_param_info bpf_dynamic_param[]; /* * Driver event notification ID */ enum ppm_driver_event_id { DEI_NONE = 0, DEI_DISABLE_DROPPING = 1, DEI_ENABLE_DROPPING = 2, }; /*! \brief Process information as returned by the PPM_IOCTL_GET_PROCLIST IOCTL. */ struct ppm_proc_info { uint64_t pid; uint64_t utime; uint64_t stime; }; struct ppm_proclist_info { int64_t n_entries; int64_t max_entries; struct ppm_proc_info entries[0]; }; enum syscall_flags { UF_NONE = 0, UF_USED = (1 << 0), UF_NEVER_DROP = (1 << 1), UF_ALWAYS_DROP = (1 << 2), UF_SIMPLEDRIVER_KEEP = (1 << 3), }; struct syscall_evt_pair { int flags; enum ppm_event_type enter_event_type; enum ppm_event_type exit_event_type; } _packed; #define SYSCALL_TABLE_SIZE 512 /* * Filler table-related definitions */ #define PPM_MAX_AUTOFILL_ARGS (1 << 2) /* * Max size of a parameter in the kernel module is u16, so no point * in going beyond 0xffff. However, in BPF the limit is more stringent * because the entire perf event must fit in u16, so make this * a more conservative 65k so we have some room for the other * parameters in the event. It shouldn't cause issues since typically * snaplen is much lower than this. */ #define PPM_MAX_ARG_SIZE 65000 struct event_filler_arguments; #include "ppm_fillers.h" struct ppm_autofill_arg { #define AF_ID_RETVAL -1 #define AF_ID_USEDEFAULT -2 int16_t id; long default_val; } _packed; enum autofill_paramtype { APT_REG, APT_SOCK, }; typedef int (*filler_callback) (struct event_filler_arguments *args); struct ppm_event_entry { filler_callback filler_callback; enum ppm_filler_id filler_id; uint16_t n_autofill_args; enum autofill_paramtype paramtype; struct ppm_autofill_arg autofill_args[PPM_MAX_AUTOFILL_ARGS]; } _packed; /* * parse_readv_writev_bufs flags */ #define PRB_FLAG_PUSH_SIZE 1 #define PRB_FLAG_PUSH_DATA 2 #define PRB_FLAG_PUSH_ALL (PRB_FLAG_PUSH_SIZE | PRB_FLAG_PUSH_DATA) #define PRB_FLAG_IS_WRITE 4 /* * Return codes */ #define PPM_SUCCESS 0 #define PPM_FAILURE_BUFFER_FULL -1 #define PPM_FAILURE_INVALID_USER_MEMORY -2 #define PPM_FAILURE_BUG -3 #define PPM_SKIP_EVENT -4 #define RW_SNAPLEN 80 #define RW_MAX_SNAPLEN PPM_MAX_ARG_SIZE #define RW_MAX_FULLCAPTURE_PORT_SNAPLEN 16000 #endif /* EVENTS_PUBLIC_H_ */ sysdig-0.26.4/driver/ppm_fillers.c000066400000000000000000003275561352731327100171060ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_CGROUPS #include #endif #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 20) #include "ppm_syscall.h" #else #include #endif #include "ppm_ringbuffer.h" #include "ppm_events_public.h" #include "ppm_events.h" #include "ppm.h" #include "ppm_flag_helpers.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) #include #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0) static inline struct inode *file_inode(struct file *f) { return f->f_path.dentry->d_inode; } #endif #define merge_64(hi, lo) ((((unsigned long long)(hi)) << 32) + ((lo) & 0xffffffffUL)) /* * Linux 5.1 kernels modify the syscall_get_arguments function to always * return all arguments rather than allowing the caller to select which * arguments are desired. This wrapper replicates the original * functionality. */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0)) #define syscall_get_arguments_deprecated syscall_get_arguments #else #define syscall_get_arguments_deprecated(_task, _reg, _start, _len, _args) \ do { \ unsigned long _sga_args[6] = {}; \ syscall_get_arguments(_task, _reg, _sga_args); \ memcpy(_args, &_sga_args[_start], _len); \ } while(0) #endif static inline struct pid_namespace *pid_ns_for_children(struct task_struct *task) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)) return task->nsproxy->pid_ns; #else return task->nsproxy->pid_ns_for_children; #endif } int f_sys_generic(struct event_filler_arguments *args) { int res; long table_index = args->syscall_id - SYSCALL_TABLE_ID0; const enum ppm_syscall_code *cur_g_syscall_code_routing_table = args->cur_g_syscall_code_routing_table; #ifdef _HAS_SOCKETCALL if (unlikely(args->syscall_id == args->socketcall_syscall)) { /* * All the socket calls should be implemented */ ASSERT(false); return PPM_FAILURE_BUG; } #endif /* * name */ if (likely(table_index >= 0 && table_index < SYSCALL_TABLE_SIZE)) { enum ppm_syscall_code sc_code = cur_g_syscall_code_routing_table[table_index]; /* * ID */ res = val_to_ring(args, sc_code, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; if (args->event_type == PPME_GENERIC_E) { /* * nativeID */ res = val_to_ring(args, args->syscall_id, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } } else { ASSERT(false); res = val_to_ring(args, (unsigned long)"", 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } return add_sentinel(args); } int f_sys_empty(struct event_filler_arguments *args) { return add_sentinel(args); } int f_sys_single(struct event_filler_arguments *args) { int res; unsigned long val; syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_single_x(struct event_filler_arguments *args) { int res; int64_t retval; retval = (int64_t)(long)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } static inline uint32_t get_fd_dev(int64_t fd) { struct files_struct *files; struct fdtable *fdt; struct file *file; struct inode *inode; struct super_block *sb; uint32_t dev = 0; if (fd < 0) return dev; files = current->files; if (unlikely(!files)) return dev; spin_lock(&files->file_lock); fdt = files_fdtable(files); if (unlikely(fd > fdt->max_fds)) goto out_unlock; file = fdt->fd[fd]; if (unlikely(!file)) goto out_unlock; inode = file_inode(file); if (unlikely(!inode)) goto out_unlock; sb = inode->i_sb; if (unlikely(!sb)) goto out_unlock; dev = new_encode_dev(sb->s_dev); out_unlock: spin_unlock(&files->file_lock); return dev; } int f_sys_open_x(struct event_filler_arguments *args) { unsigned long val; unsigned long flags; unsigned long modes; int res; int64_t retval; /* * fd */ retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * name */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Flags * Note that we convert them into the ppm portable representation before pushing them to the ring */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &flags); res = val_to_ring(args, open_flags_to_scap(flags), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * mode */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &modes); res = val_to_ring(args, open_modes_to_scap(flags, modes), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * dev */ res = val_to_ring(args, get_fd_dev(retval), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_read_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_t retval; unsigned long bufsize; /* * Retrieve the FD. It will be used for dynamic snaplen calculation. */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); args->fd = (int)val; /* * res */ retval = (int64_t)(long)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * data */ if (retval < 0) { /* * The operation failed, return an empty buffer */ val = 0; bufsize = 0; } else { syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); /* * The return value can be lower than the value provided by the user, * and we take that into account. */ bufsize = retval; } /* * Copy the buffer */ args->enforce_snaplen = true; res = val_to_ring(args, val, bufsize, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_write_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_t retval; unsigned long bufsize; /* * Retrieve the FD. It will be used for dynamic snaplen calculation. */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); args->fd = (int)val; /* * res */ retval = (int64_t)(long)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * data */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); bufsize = val; /* * Copy the buffer */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); args->enforce_snaplen = true; res = val_to_ring(args, val, bufsize, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } /* * get_mm_counter was not inline and exported between 3.0 and 3.4 * https://github.com/torvalds/linux/commit/69c978232aaa99476f9bd002c2a29a84fa3779b5 * Hence the crap in these two functions */ unsigned long ppm_get_mm_counter(struct mm_struct *mm, int member) { long val = 0; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) val = get_mm_counter(mm, member); #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) val = atomic_long_read(&mm->rss_stat.count[member]); if (val < 0) val = 0; #endif return val; } static unsigned long ppm_get_mm_swap(struct mm_struct *mm) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) return ppm_get_mm_counter(mm, MM_SWAPENTS); #endif return 0; } static unsigned long ppm_get_mm_rss(struct mm_struct *mm) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) return get_mm_rss(mm); #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) return ppm_get_mm_counter(mm, MM_FILEPAGES) + ppm_get_mm_counter(mm, MM_ANONPAGES); #else return get_mm_rss(mm); #endif return 0; } #ifdef CONFIG_CGROUPS #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) static int ppm_cgroup_path(const struct cgroup *cgrp, char *buf, int buflen) { char *start; struct dentry *dentry = rcu_dereference(cgrp->dentry); if (!dentry) { /* * Inactive subsystems have no dentry for their root * cgroup */ strcpy(buf, "/"); return 0; } start = buf + buflen; *--start = '\0'; for (;;) { int len = dentry->d_name.len; start -= len; if (start < buf) return -ENAMETOOLONG; memcpy(start, cgrp->dentry->d_name.name, len); cgrp = cgrp->parent; if (!cgrp) break; dentry = rcu_dereference(cgrp->dentry); if (!cgrp->parent) continue; if (--start < buf) return -ENAMETOOLONG; *start = '/'; } memmove(buf, start, buf + buflen - start); return 0; } #endif static int append_cgroup(const char *subsys_name, int subsys_id, char *buf, int *available) { int pathlen; int subsys_len; char *path; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) || LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) int res; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) struct cgroup_subsys_state *css = task_css(current, subsys_id); #else struct cgroup_subsys_state *css = task_subsys_state(current, subsys_id); #endif if (!css) { ASSERT(false); return 1; } if (!css->cgroup) { ASSERT(false); return 1; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) // According to https://github.com/torvalds/linux/commit/4c737b41de7f4eef2a593803bad1b918dd718b10 // cgroup_path now returns an int again res = cgroup_path(css->cgroup, buf, *available); if (res < 0) { ASSERT(false); path = "NA"; } else { path = buf; } #elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) path = cgroup_path(css->cgroup, buf, *available); if (!path) { ASSERT(false); path = "NA"; } #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) res = cgroup_path(css->cgroup, buf, *available); if (res < 0) { ASSERT(false); path = "NA"; } else { path = buf; } #else res = ppm_cgroup_path(css->cgroup, buf, *available); if (res < 0) { ASSERT(false); path = "NA"; } else { path = buf; } #endif pathlen = strlen(path); subsys_len = strlen(subsys_name); if (subsys_len + 1 + pathlen + 1 > *available) return 1; memmove(buf + subsys_len + 1, path, pathlen); memcpy(buf, subsys_name, subsys_len); buf += subsys_len; *buf++ = '='; buf += pathlen; *buf++ = 0; *available -= (subsys_len + 1 + pathlen + 1); return 0; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) #define SUBSYS(_x) \ if (append_cgroup(#_x, _x ## _cgrp_id, args->str_storage + STR_STORAGE_SIZE - available, &available)) \ goto cgroups_error; #elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) #define IS_SUBSYS_ENABLED(option) IS_BUILTIN(option) #define SUBSYS(_x) \ if (append_cgroup(#_x, _x ## _subsys_id, args->str_storage + STR_STORAGE_SIZE - available, &available)) \ goto cgroups_error; #elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) #define IS_SUBSYS_ENABLED(option) IS_ENABLED(option) #define SUBSYS(_x) \ if (append_cgroup(#_x, _x ## _subsys_id, args->str_storage + STR_STORAGE_SIZE - available, &available)) \ goto cgroups_error; #else #define SUBSYS(_x) \ if (append_cgroup(#_x, _x ## _subsys_id, args->str_storage + STR_STORAGE_SIZE - available, &available)) \ goto cgroups_error; #endif #endif /* Takes in a NULL-terminated array of pointers to strings in userspace, and * concatenates them to a single \0-separated string. Return the length of this * string, or <0 on error */ static int accumulate_argv_or_env(const char __user * __user *argv, char *str_storage, int available) { int len = 0; int n_bytes_copied; if (argv == NULL) return len; for (;;) { const char __user *p; if (unlikely(ppm_get_user(p, argv))) return PPM_FAILURE_INVALID_USER_MEMORY; if (p == NULL) break; /* need at least enough space for a \0 */ if (available < 1) return PPM_FAILURE_BUFFER_FULL; n_bytes_copied = ppm_strncpy_from_user(&str_storage[len], p, available); /* ppm_strncpy_from_user includes the trailing \0 in its return * count. I want to pretend it was strncpy_from_user() so I * subtract off the 1 */ n_bytes_copied--; if (n_bytes_copied < 0) return PPM_FAILURE_INVALID_USER_MEMORY; if (n_bytes_copied >= available) return PPM_FAILURE_BUFFER_FULL; /* update buffer. I want to keep the trailing \0, so I +1 */ available -= n_bytes_copied+1; len += n_bytes_copied+1; argv++; } return len; } #ifdef CONFIG_COMPAT /* compat version that deals correctly with 32bits pointers of argv */ static int compat_accumulate_argv_or_env(compat_uptr_t argv, char *str_storage, int available) { int len = 0; int n_bytes_copied; if (compat_ptr(argv) == NULL) return len; for (;;) { compat_uptr_t compat_p; const char __user *p; if (unlikely(ppm_get_user(compat_p, compat_ptr(argv)))) return PPM_FAILURE_INVALID_USER_MEMORY; p = compat_ptr(compat_p); if (p == NULL) break; /* need at least enough space for a \0 */ if (available < 1) return PPM_FAILURE_BUFFER_FULL; n_bytes_copied = ppm_strncpy_from_user(&str_storage[len], p, available); /* ppm_strncpy_from_user includes the trailing \0 in its return * count. I want to pretend it was strncpy_from_user() so I * subtract off the 1 */ n_bytes_copied--; if (n_bytes_copied < 0) { return PPM_FAILURE_INVALID_USER_MEMORY; } if (n_bytes_copied >= available) return PPM_FAILURE_BUFFER_FULL; /* update buffer. I want to keep the trailing \0, so I +1 */ available -= n_bytes_copied+1; len += n_bytes_copied+1; argv += sizeof(compat_uptr_t); } return len; } #endif // probe_kernel_read() only added in kernel 2.6.26 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) long probe_kernel_read(void *dst, const void *src, size_t size) { long ret; mm_segment_t old_fs = get_fs(); set_fs(KERNEL_DS); pagefault_disable(); ret = __copy_from_user_inatomic(dst, (__force const void __user *)src, size); pagefault_enable(); set_fs(old_fs); return ret ? -EFAULT : 0; } #endif static int ppm_get_tty(void) { /* Locking of the signal structures seems too complicated across * multiple kernel versions to get it right, so simply do protected * memory accesses, and in the worst case we get some garbage, * which is not the end of the world. In the vast majority of accesses, * we'll be just fine. */ struct signal_struct *sig; struct tty_struct *tty; struct tty_driver *driver; int major; int minor_start; int index; int tty_nr = 0; sig = current->signal; if (!sig) return 0; if (unlikely(probe_kernel_read(&tty, &sig->tty, sizeof(tty)))) return 0; if (!tty) return 0; if (unlikely(probe_kernel_read(&index, &tty->index, sizeof(index)))) return 0; if (unlikely(probe_kernel_read(&driver, &tty->driver, sizeof(driver)))) return 0; if (!driver) return 0; if (unlikely(probe_kernel_read(&major, &driver->major, sizeof(major)))) return 0; if (unlikely(probe_kernel_read(&minor_start, &driver->minor_start, sizeof(minor_start)))) return 0; tty_nr = new_encode_dev(MKDEV(major, minor_start) + index); return tty_nr; } int f_proc_startupdate(struct event_filler_arguments *args) { unsigned long val; int res = 0; unsigned int exe_len = 0; /* the length of the executable string */ int args_len = 0; /*the combined length of the arguments string + executable string */ struct mm_struct *mm = current->mm; int64_t retval; int ptid; char *spwd = ""; long total_vm = 0; long total_rss = 0; long swap = 0; int available = STR_STORAGE_SIZE; /* * Make sure the operation was successful */ retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; if (unlikely(retval < 0 && args->event_type != PPME_SYSCALL_EXECVE_19_X)) { /* The call failed, but this syscall has no exe, args * anyway, so I report empty ones */ *args->str_storage = 0; /* * exe */ res = val_to_ring(args, (uint64_t)(long)args->str_storage, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Args */ res = val_to_ring(args, (int64_t)(long)args->str_storage, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } else { if (likely(retval >= 0)) { /* * The call succeeded. Get exe, args from the current * process; put one \0-separated exe-args string into * str_storage */ if (unlikely(!mm)) { args->str_storage[0] = 0; pr_info("f_proc_startupdate drop, mm=NULL\n"); return PPM_FAILURE_BUG; } if (unlikely(!mm->arg_end)) { args->str_storage[0] = 0; pr_info("f_proc_startupdate drop, mm->arg_end=NULL\n"); return PPM_FAILURE_BUG; } args_len = mm->arg_end - mm->arg_start; if (args_len) { if (args_len > PAGE_SIZE) args_len = PAGE_SIZE; if (unlikely(ppm_copy_from_user(args->str_storage, (const void __user *)mm->arg_start, args_len))) args_len = 0; else args->str_storage[args_len - 1] = 0; } } else { /* * The execve call failed. I get exe, args from the * input args; put one \0-separated exe-args string into * str_storage */ args->str_storage[0] = 0; syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); #ifdef CONFIG_COMPAT if (unlikely(args->compat)) args_len = compat_accumulate_argv_or_env((compat_uptr_t)val, args->str_storage, available); else #endif args_len = accumulate_argv_or_env((const char __user * __user *)val, args->str_storage, available); if (unlikely(args_len < 0)) args_len = 0; } if (args_len == 0) *args->str_storage = 0; exe_len = strnlen(args->str_storage, args_len); if (exe_len < args_len) ++exe_len; /* * exe */ res = val_to_ring(args, (uint64_t)(long)args->str_storage, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Args */ res = val_to_ring(args, (int64_t)(long)args->str_storage + exe_len, args_len - exe_len, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } /* * tid */ res = val_to_ring(args, (int64_t)current->pid, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * pid */ res = val_to_ring(args, (int64_t)current->tgid, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * ptid */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) if (current->real_parent) ptid = current->real_parent->pid; #else if (current->parent) ptid = current->parent->pid; #endif else ptid = 0; res = val_to_ring(args, (int64_t)ptid, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * cwd, pushed empty to avoid breaking compatibility * with the older event format */ res = val_to_ring(args, (uint64_t)(long)spwd, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * fdlimit */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) res = val_to_ring(args, (int64_t)rlimit(RLIMIT_NOFILE), 0, false, 0); #else res = val_to_ring(args, (int64_t)0, 0, false, 0); #endif if (res != PPM_SUCCESS) return res; /* * pgft_maj */ res = val_to_ring(args, current->maj_flt, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * pgft_min */ res = val_to_ring(args, current->min_flt, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; if (mm) { total_vm = mm->total_vm << (PAGE_SHIFT-10); total_rss = ppm_get_mm_rss(mm) << (PAGE_SHIFT-10); swap = ppm_get_mm_swap(mm) << (PAGE_SHIFT-10); } /* * vm_size */ res = val_to_ring(args, total_vm, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * vm_rss */ res = val_to_ring(args, total_rss, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * vm_swap */ res = val_to_ring(args, swap, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * comm */ res = val_to_ring(args, (uint64_t)current->comm, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * cgroups */ args->str_storage[0] = 0; #ifdef CONFIG_CGROUPS rcu_read_lock(); #include cgroups_error: rcu_read_unlock(); #endif res = val_to_ring(args, (int64_t)(long)args->str_storage, STR_STORAGE_SIZE - available, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; if (args->event_type == PPME_SYSCALL_CLONE_20_X || args->event_type == PPME_SYSCALL_FORK_20_X || args->event_type == PPME_SYSCALL_VFORK_20_X) { /* * clone-only parameters */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) uint64_t euid = from_kuid_munged(current_user_ns(), current_euid()); uint64_t egid = from_kgid_munged(current_user_ns(), current_egid()); #elif LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) uint64_t euid = current_euid(); uint64_t egid = current_egid(); #else uint64_t euid = current->euid; uint64_t egid = current->egid; #endif int64_t in_pidns = 0; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) struct pid_namespace *pidns = task_active_pid_ns(current); #endif /* * flags */ if (args->event_type == PPME_SYSCALL_CLONE_20_X) { #ifdef CONFIG_S390 syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); #else syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); #endif } else val = 0; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) if(pidns != &init_pid_ns || pid_ns_for_children(current) != pidns) in_pidns = PPM_CL_CHILD_IN_PIDNS; #endif res = val_to_ring(args, (uint64_t)clone_flags_to_scap(val) | in_pidns, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * uid */ res = val_to_ring(args, euid, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * gid */ res = val_to_ring(args, egid, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * vtid */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) res = val_to_ring(args, task_pid_vnr(current), 0, false, 0); #else /* Not relevant in old kernels */ res = val_to_ring(args, 0, 0, false, 0); #endif if (unlikely(res != PPM_SUCCESS)) return res; /* * vpid */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) res = val_to_ring(args, task_tgid_vnr(current), 0, false, 0); #else /* Not relevant in old kernels */ res = val_to_ring(args, 0, 0, false, 0); #endif if (unlikely(res != PPM_SUCCESS)) return res; } else if (args->event_type == PPME_SYSCALL_EXECVE_19_X) { /* * execve-only parameters */ long env_len = 0; int tty_nr = 0; if (likely(retval >= 0)) { /* * Already checked for mm validity */ env_len = mm->env_end - mm->env_start; if (env_len) { if (env_len > PAGE_SIZE) env_len = PAGE_SIZE; if (unlikely(ppm_copy_from_user(args->str_storage, (const void __user *)mm->env_start, env_len))) env_len = 0; else args->str_storage[env_len - 1] = 0; } } else { /* * The call failed, so get the env from the arguments */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); #ifdef CONFIG_COMPAT if (unlikely(args->compat)) env_len = compat_accumulate_argv_or_env((compat_uptr_t)val, args->str_storage, available); else #endif env_len = accumulate_argv_or_env((const char __user * __user *)val, args->str_storage, available); if (unlikely(env_len < 0)) env_len = 0; } if (env_len == 0) *args->str_storage = 0; /* * environ */ res = val_to_ring(args, (int64_t)(long)args->str_storage, env_len, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * tty */ tty_nr = ppm_get_tty(); res = val_to_ring(args, tty_nr, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * pgid */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) res = val_to_ring(args, (int64_t)task_pgrp_nr_ns(current, task_active_pid_ns(current)), 0, false, 0); #else res = val_to_ring(args, (int64_t)process_group(current), 0, false, 0); #endif if (unlikely(res != PPM_SUCCESS)) return res; /* * loginuid */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) val = from_kuid(current_user_ns(), audit_get_loginuid(current)); #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) val = audit_get_loginuid(current); #else val = audit_get_loginuid(current->audit_context); #endif res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } return add_sentinel(args); } int f_sys_execve_e(struct event_filler_arguments *args) { int res; unsigned long val; /* * filename */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (res == PPM_FAILURE_INVALID_USER_MEMORY) res = val_to_ring(args, (unsigned long)"", 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_socket_bind_x(struct event_filler_arguments *args) { int res; int64_t retval; int err = 0; u16 size = 0; struct sockaddr __user *usrsockaddr; unsigned long val; struct sockaddr_storage address; char *targetbuf = args->str_storage; /* * res */ retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); /* * addr */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); else val = args->socketcall_args[1]; usrsockaddr = (struct sockaddr __user *)val; /* * Get the address len */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); else val = args->socketcall_args[2]; if (usrsockaddr != NULL && val != 0) { /* * Copy the address */ err = addr_to_kernel(usrsockaddr, val, (struct sockaddr *)&address); if (likely(err >= 0)) { /* * Convert the fd into socket endpoint information */ size = pack_addr((struct sockaddr *)&address, val, targetbuf, STR_STORAGE_SIZE); } } /* * Copy the endpoint info into the ring */ res = val_to_ring(args, (uint64_t)(unsigned long)targetbuf, size, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_connect_x(struct event_filler_arguments *args) { int res; int64_t retval; int err = 0; int fd; struct sockaddr __user *usrsockaddr; u16 size = 0; char *targetbuf = args->str_storage; struct sockaddr_storage address; unsigned long val; /* * Push the result */ retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); /* * Retrieve the fd and push it to the ring. * Note that, even if we are in the exit callback, the arguments are still * in the stack, and therefore we can consume them. */ if (!args->is_socketcall) { syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); fd = (int)val; } else fd = (int)args->socketcall_args[0]; if (fd >= 0) { /* * Get the address */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); else val = args->socketcall_args[1]; usrsockaddr = (struct sockaddr __user *)val; /* * Get the address len */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); else val = args->socketcall_args[2]; if (usrsockaddr != NULL && val != 0) { /* * Copy the address */ err = addr_to_kernel(usrsockaddr, val, (struct sockaddr *)&address); if (likely(err >= 0)) { /* * Convert the fd into socket endpoint information */ size = fd_to_socktuple(fd, (struct sockaddr *)&address, val, true, false, targetbuf, STR_STORAGE_SIZE); } } } /* * Copy the endpoint info into the ring */ res = val_to_ring(args, (uint64_t)(unsigned long)targetbuf, size, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_socketpair_x(struct event_filler_arguments *args) { int res; int64_t retval; unsigned long val; int fds[2]; int err; struct socket *sock; struct unix_sock *us; struct sock *speer; /* * retval */ retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * If the call was successful, copy the FDs */ if (likely(retval >= 0)) { /* * fds */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 3, 1, &val); else val = args->socketcall_args[3]; #ifdef CONFIG_COMPAT if (!args->compat) { #endif if (unlikely(ppm_copy_from_user(fds, (const void __user *)val, sizeof(fds)))) return PPM_FAILURE_INVALID_USER_MEMORY; #ifdef CONFIG_COMPAT } else { if (unlikely(ppm_copy_from_user(fds, (const void __user *)compat_ptr(val), sizeof(fds)))) return PPM_FAILURE_INVALID_USER_MEMORY; } #endif res = val_to_ring(args, fds[0], 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; res = val_to_ring(args, fds[1], 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* get socket source and peer address */ sock = sockfd_lookup(fds[0], &err); if (likely(sock != NULL)) { us = unix_sk(sock->sk); speer = us->peer; res = val_to_ring(args, (unsigned long)us, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) { sockfd_put(sock); return res; } res = val_to_ring(args, (unsigned long)speer, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) { sockfd_put(sock); return res; } sockfd_put(sock); } else { return err; } } else { res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } return add_sentinel(args); } static int parse_sockopt(struct event_filler_arguments *args, int level, int optname, const void __user *optval, int optlen) { union { uint32_t val32; uint64_t val64; struct timeval tv; } u; if (level == SOL_SOCKET) { switch (optname) { #ifdef SO_ERROR case SO_ERROR: if (unlikely(ppm_copy_from_user(&u.val32, optval, sizeof(u.val32)))) return PPM_FAILURE_INVALID_USER_MEMORY; return val_to_ring(args, -(int)u.val32, 0, false, PPM_SOCKOPT_IDX_ERRNO); #endif #ifdef SO_RCVTIMEO case SO_RCVTIMEO: #endif #ifdef SO_SNDTIMEO case SO_SNDTIMEO: #endif if (unlikely(ppm_copy_from_user(&u.tv, optval, sizeof(u.tv)))) return PPM_FAILURE_INVALID_USER_MEMORY; return val_to_ring(args, u.tv.tv_sec * 1000000000 + u.tv.tv_usec * 1000, 0, false, PPM_SOCKOPT_IDX_TIMEVAL); #ifdef SO_COOKIE case SO_COOKIE: if (unlikely(ppm_copy_from_user(&u.val64, optval, sizeof(u.val64)))) return PPM_FAILURE_INVALID_USER_MEMORY; return val_to_ring(args, u.val64, 0, false, PPM_SOCKOPT_IDX_UINT64); #endif #ifdef SO_DEBUG case SO_DEBUG: #endif #ifdef SO_REUSEADDR case SO_REUSEADDR: #endif #ifdef SO_TYPE case SO_TYPE: #endif #ifdef SO_DONTROUTE case SO_DONTROUTE: #endif #ifdef SO_BROADCAST case SO_BROADCAST: #endif #ifdef SO_SNDBUF case SO_SNDBUF: #endif #ifdef SO_RCVBUF case SO_RCVBUF: #endif #ifdef SO_SNDBUFFORCE case SO_SNDBUFFORCE: #endif #ifdef SO_RCVBUFFORCE case SO_RCVBUFFORCE: #endif #ifdef SO_KEEPALIVE case SO_KEEPALIVE: #endif #ifdef SO_OOBINLINE case SO_OOBINLINE: #endif #ifdef SO_NO_CHECK case SO_NO_CHECK: #endif #ifdef SO_PRIORITY case SO_PRIORITY: #endif #ifdef SO_BSDCOMPAT case SO_BSDCOMPAT: #endif #ifdef SO_REUSEPORT case SO_REUSEPORT: #endif #ifdef SO_PASSCRED case SO_PASSCRED: #endif #ifdef SO_RCVLOWAT case SO_RCVLOWAT: #endif #ifdef SO_SNDLOWAT case SO_SNDLOWAT: #endif #ifdef SO_SECURITY_AUTHENTICATION case SO_SECURITY_AUTHENTICATION: #endif #ifdef SO_SECURITY_ENCRYPTION_TRANSPORT case SO_SECURITY_ENCRYPTION_TRANSPORT: #endif #ifdef SO_SECURITY_ENCRYPTION_NETWORK case SO_SECURITY_ENCRYPTION_NETWORK: #endif #ifdef SO_BINDTODEVICE case SO_BINDTODEVICE: #endif #ifdef SO_DETACH_FILTER case SO_DETACH_FILTER: #endif #ifdef SO_TIMESTAMP case SO_TIMESTAMP: #endif #ifdef SO_ACCEPTCONN case SO_ACCEPTCONN: #endif #ifdef SO_PEERSEC case SO_PEERSEC: #endif #ifdef SO_PASSSEC case SO_PASSSEC: #endif #ifdef SO_TIMESTAMPNS case SO_TIMESTAMPNS: #endif #ifdef SO_MARK case SO_MARK: #endif #ifdef SO_TIMESTAMPING case SO_TIMESTAMPING: #endif #ifdef SO_PROTOCOL case SO_PROTOCOL: #endif #ifdef SO_DOMAIN case SO_DOMAIN: #endif #ifdef SO_RXQ_OVFL case SO_RXQ_OVFL: #endif #ifdef SO_WIFI_STATUS case SO_WIFI_STATUS: #endif #ifdef SO_PEEK_OFF case SO_PEEK_OFF: #endif #ifdef SO_NOFCS case SO_NOFCS: #endif #ifdef SO_LOCK_FILTER case SO_LOCK_FILTER: #endif #ifdef SO_SELECT_ERR_QUEUE case SO_SELECT_ERR_QUEUE: #endif #ifdef SO_BUSY_POLL case SO_BUSY_POLL: #endif #ifdef SO_MAX_PACING_RATE case SO_MAX_PACING_RATE: #endif #ifdef SO_BPF_EXTENSIONS case SO_BPF_EXTENSIONS: #endif #ifdef SO_INCOMING_CPU case SO_INCOMING_CPU: #endif if (unlikely(ppm_copy_from_user(&u.val32, optval, sizeof(u.val32)))) return PPM_FAILURE_INVALID_USER_MEMORY; return val_to_ring(args, u.val32, 0, false, PPM_SOCKOPT_IDX_UINT32); default: return val_to_ring(args, (unsigned long)optval, optlen, true, PPM_SOCKOPT_IDX_UNKNOWN); } } else { return val_to_ring(args, (unsigned long)optval, optlen, true, PPM_SOCKOPT_IDX_UNKNOWN); } } int f_sys_setsockopt_x(struct event_filler_arguments *args) { int res; int64_t retval; unsigned long val[5] = {}; syscall_get_arguments_deprecated(current, args->regs, 0, 5, val); retval = (int64_t)(long)syscall_get_return_value(current, args->regs); /* retval */ res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* fd */ res = val_to_ring(args, val[0], 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* level */ res = val_to_ring(args, sockopt_level_to_scap(val[1]), 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* optname */ res = val_to_ring(args, sockopt_optname_to_scap(val[1], val[2]), 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* optval */ res = parse_sockopt(args, val[1], val[2], (const void __user*)val[3], val[4]); if (unlikely(res != PPM_SUCCESS)) return res; /* optlen */ res = val_to_ring(args, val[4], 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_getsockopt_x(struct event_filler_arguments *args) { int res; int64_t retval; uint32_t optlen; unsigned long val[5] = {}; syscall_get_arguments_deprecated(current, args->regs, 0, 5, val); retval = (int64_t)(long)syscall_get_return_value(current, args->regs); /* retval */ res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* fd */ res = val_to_ring(args, val[0], 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* level */ res = val_to_ring(args, sockopt_level_to_scap(val[1]), 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* optname */ res = val_to_ring(args, sockopt_optname_to_scap(val[1], val[2]), 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; if (unlikely(ppm_copy_from_user(&optlen, (const void __user*)val[4], sizeof(optlen)))) return PPM_FAILURE_INVALID_USER_MEMORY; /* optval */ res = parse_sockopt(args, val[1], val[2], (const void __user*)val[3], optlen); if (unlikely(res != PPM_SUCCESS)) return res; /* optlen */ res = val_to_ring(args, optlen, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_accept4_e(struct event_filler_arguments *args) { int res; /* * push the flags into the ring. * XXX we don't support flags yet and so we just return zero */ /* res = val_to_ring(args, args->socketcall_args[3]); */ res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_accept_x(struct event_filler_arguments *args) { int res; int fd; char *targetbuf = args->str_storage; u16 size = 0; unsigned long queuepct = 0; unsigned long ack_backlog = 0; unsigned long max_ack_backlog = 0; unsigned long srvskfd; int err = 0; struct socket *sock; /* * Push the fd */ fd = syscall_get_return_value(current, args->regs); res = val_to_ring(args, (int64_t)fd, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Convert the fd into socket endpoint information */ size = fd_to_socktuple(fd, NULL, 0, false, true, targetbuf, STR_STORAGE_SIZE); /* * Copy the endpoint info into the ring */ res = val_to_ring(args, (uint64_t)(unsigned long)targetbuf, size, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * queuepct */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 0, 1, &srvskfd); else srvskfd = args->socketcall_args[0]; sock = sockfd_lookup(srvskfd, &err); if (sock && sock->sk) { ack_backlog = sock->sk->sk_ack_backlog; max_ack_backlog = sock->sk->sk_max_ack_backlog; } if (sock) sockfd_put(sock); if (max_ack_backlog) queuepct = (unsigned long)ack_backlog * 100 / max_ack_backlog; res = val_to_ring(args, queuepct, 0, false, 0); if (res != PPM_SUCCESS) return res; res = val_to_ring(args, ack_backlog, 0, false, 0); if (res != PPM_SUCCESS) return res; res = val_to_ring(args, max_ack_backlog, 0, false, 0); if (res != PPM_SUCCESS) return res; return add_sentinel(args); } int f_sys_send_e_common(struct event_filler_arguments *args, int *fd) { int res; unsigned long size; unsigned long val; /* * fd */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); else val = args->socketcall_args[0]; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; *fd = val; /* * size */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 2, 1, &size); else size = args->socketcall_args[2]; res = val_to_ring(args, size, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return PPM_SUCCESS; } int f_sys_send_e(struct event_filler_arguments *args) { int res; int fd; res = f_sys_send_e_common(args, &fd); if (likely(res == PPM_SUCCESS)) return add_sentinel(args); return res; } int f_sys_sendto_e(struct event_filler_arguments *args) { unsigned long val; int res; u16 size = 0; char *targetbuf = args->str_storage; int fd; struct sockaddr __user *usrsockaddr; struct sockaddr_storage address; int err = 0; *targetbuf = 250; /* * Push the common params to the ring */ res = f_sys_send_e_common(args, &fd); if (unlikely(res != PPM_SUCCESS)) return res; /* * Get the address */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 4, 1, &val); else val = args->socketcall_args[4]; usrsockaddr = (struct sockaddr __user *)val; /* * Get the address len */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 5, 1, &val); else val = args->socketcall_args[5]; if (usrsockaddr != NULL && val != 0) { /* * Copy the address */ err = addr_to_kernel(usrsockaddr, val, (struct sockaddr *)&address); if (likely(err >= 0)) { /* * Convert the fd into socket endpoint information */ size = fd_to_socktuple(fd, (struct sockaddr *)&address, val, true, false, targetbuf, STR_STORAGE_SIZE); } } /* * Copy the endpoint info into the ring */ res = val_to_ring(args, (uint64_t)(unsigned long)targetbuf, size, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_send_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_t retval; unsigned long bufsize; /* * Retrieve the FD. It will be used for dynamic snaplen calculation. */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); else val = args->socketcall_args[0]; args->fd = (int)val; /* * res */ retval = (int64_t)(long)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * data */ if (retval < 0) { /* * The operation failed, return an empty buffer */ val = 0; bufsize = 0; } else { if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); else val = args->socketcall_args[1]; /* * The return value can be lower than the value provided by the user, * and we take that into account. */ bufsize = retval; } args->enforce_snaplen = true; res = val_to_ring(args, val, bufsize, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_recv_x_common(struct event_filler_arguments *args, int64_t *retval) { int res; unsigned long val; unsigned long bufsize; /* * Retrieve the FD. It will be used for dynamic snaplen calculation. */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); else val = args->socketcall_args[1]; args->fd = (int)val; /* * res */ *retval = (int64_t)(long)syscall_get_return_value(current, args->regs); res = val_to_ring(args, *retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * data */ if (*retval < 0) { /* * The operation failed, return an empty buffer */ val = 0; bufsize = 0; } else { if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); else val = args->socketcall_args[1]; /* * The return value can be lower than the value provided by the user, * and we take that into account. */ bufsize = *retval; } args->enforce_snaplen = true; res = val_to_ring(args, val, bufsize, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return PPM_SUCCESS; } int f_sys_recv_x(struct event_filler_arguments *args) { int res; int64_t retval; res = f_sys_recv_x_common(args, &retval); if (likely(res == PPM_SUCCESS)) return add_sentinel(args); return res; } int f_sys_recvfrom_x(struct event_filler_arguments *args) { unsigned long val; int res; u16 size = 0; int64_t retval; char *targetbuf = args->str_storage; int fd; struct sockaddr __user *usrsockaddr; struct sockaddr_storage address; int addrlen; int err = 0; /* * Push the common params to the ring */ res = f_sys_recv_x_common(args, &retval); if (unlikely(res != PPM_SUCCESS)) return res; if (retval >= 0) { /* * Get the fd */ if (!args->is_socketcall) { syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); fd = (int)val; } else fd = (int)args->socketcall_args[0]; /* * Get the address */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 4, 1, &val); else val = args->socketcall_args[4]; usrsockaddr = (struct sockaddr __user *)val; /* * Get the address len */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 5, 1, &val); else val = args->socketcall_args[5]; if (usrsockaddr != NULL && val != 0) { #ifdef CONFIG_COMPAT if (!args->compat) { #endif if (unlikely(ppm_copy_from_user(&addrlen, (const void __user *)val, sizeof(addrlen)))) return PPM_FAILURE_INVALID_USER_MEMORY; #ifdef CONFIG_COMPAT } else { if (unlikely(ppm_copy_from_user(&addrlen, (const void __user *)compat_ptr(val), sizeof(addrlen)))) return PPM_FAILURE_INVALID_USER_MEMORY; } #endif /* * Copy the address */ err = addr_to_kernel(usrsockaddr, addrlen, (struct sockaddr *)&address); if (likely(err >= 0)) { /* * Convert the fd into socket endpoint information */ size = fd_to_socktuple(fd, (struct sockaddr *)&address, addrlen, true, true, targetbuf, STR_STORAGE_SIZE); } } } /* * Copy the endpoint info into the ring */ res = val_to_ring(args, (uint64_t)(unsigned long)targetbuf, size, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_sendmsg_e(struct event_filler_arguments *args) { int res; unsigned long val; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) struct user_msghdr mh; #else struct msghdr mh; #endif char *targetbuf = args->str_storage; const struct iovec __user *iov; #ifdef CONFIG_COMPAT const struct compat_iovec __user *compat_iov; struct compat_msghdr compat_mh; #endif unsigned long iovcnt; int fd; u16 size = 0; int addrlen; int err = 0; struct sockaddr __user *usrsockaddr; struct sockaddr_storage address; /* * fd */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); else val = args->socketcall_args[0]; fd = val; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Retrieve the message header */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); else val = args->socketcall_args[1]; #ifdef CONFIG_COMPAT if (!args->compat) { #endif if (unlikely(ppm_copy_from_user(&mh, (const void __user *)val, sizeof(mh)))) return PPM_FAILURE_INVALID_USER_MEMORY; /* * size */ iov = (const struct iovec __user *)mh.msg_iov; iovcnt = mh.msg_iovlen; res = parse_readv_writev_bufs(args, iov, iovcnt, args->consumer->snaplen, PRB_FLAG_PUSH_SIZE | PRB_FLAG_IS_WRITE); if (unlikely(res != PPM_SUCCESS)) return res; /* * tuple */ usrsockaddr = (struct sockaddr __user *)mh.msg_name; addrlen = mh.msg_namelen; #ifdef CONFIG_COMPAT } else { if (unlikely(ppm_copy_from_user(&compat_mh, (const void __user *)compat_ptr(val), sizeof(compat_mh)))) return PPM_FAILURE_INVALID_USER_MEMORY; /* * size */ compat_iov = (const struct compat_iovec __user *)compat_ptr(compat_mh.msg_iov); iovcnt = compat_mh.msg_iovlen; res = compat_parse_readv_writev_bufs(args, compat_iov, iovcnt, args->consumer->snaplen, PRB_FLAG_PUSH_SIZE | PRB_FLAG_IS_WRITE); if (unlikely(res != PPM_SUCCESS)) return res; /* * tuple */ usrsockaddr = (struct sockaddr __user *)compat_ptr(compat_mh.msg_name); addrlen = compat_mh.msg_namelen; } #endif if (usrsockaddr != NULL && addrlen != 0) { /* * Copy the address */ err = addr_to_kernel(usrsockaddr, addrlen, (struct sockaddr *)&address); if (likely(err >= 0)) { /* * Convert the fd into socket endpoint information */ size = fd_to_socktuple(fd, (struct sockaddr *)&address, addrlen, true, false, targetbuf, STR_STORAGE_SIZE); } } /* Copy the endpoint info into the ring */ res = val_to_ring(args, (uint64_t)(unsigned long)targetbuf, size, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_sendmsg_x(struct event_filler_arguments *args) { int res; unsigned long val; int64_t retval; const struct iovec __user *iov; #ifdef CONFIG_COMPAT const struct compat_iovec __user *compat_iov; struct compat_msghdr compat_mh; #endif unsigned long iovcnt; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) struct user_msghdr mh; #else struct msghdr mh; #endif /* * res */ retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Retrieve the message header */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); else val = args->socketcall_args[1]; /* * data */ #ifdef CONFIG_COMPAT if (!args->compat) { #endif if (unlikely(ppm_copy_from_user(&mh, (const void __user *)val, sizeof(mh)))) return PPM_FAILURE_INVALID_USER_MEMORY; iov = (const struct iovec __user *)mh.msg_iov; iovcnt = mh.msg_iovlen; res = parse_readv_writev_bufs(args, iov, iovcnt, args->consumer->snaplen, PRB_FLAG_PUSH_DATA | PRB_FLAG_IS_WRITE); if (unlikely(res != PPM_SUCCESS)) return res; #ifdef CONFIG_COMPAT } else { if (unlikely(ppm_copy_from_user(&compat_mh, (const void __user *)compat_ptr(val), sizeof(compat_mh)))) return PPM_FAILURE_INVALID_USER_MEMORY; compat_iov = (const struct compat_iovec __user *)compat_ptr(compat_mh.msg_iov); iovcnt = compat_mh.msg_iovlen; res = compat_parse_readv_writev_bufs(args, compat_iov, iovcnt, args->consumer->snaplen, PRB_FLAG_PUSH_DATA | PRB_FLAG_IS_WRITE); if (unlikely(res != PPM_SUCCESS)) return res; } #endif return add_sentinel(args); } int f_sys_recvmsg_x(struct event_filler_arguments *args) { int res; unsigned long val; int64_t retval; const struct iovec __user *iov; #ifdef CONFIG_COMPAT const struct compat_iovec __user *compat_iov; struct compat_msghdr compat_mh; #endif unsigned long iovcnt; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) struct user_msghdr mh; #else struct msghdr mh; #endif char *targetbuf = args->str_storage; int fd; struct sockaddr __user *usrsockaddr; struct sockaddr_storage address; u16 size = 0; int addrlen; int err = 0; /* * res */ retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Retrieve the message header */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); else val = args->socketcall_args[1]; #ifdef CONFIG_COMPAT if (!args->compat) { #endif if (unlikely(ppm_copy_from_user(&mh, (const void __user *)val, sizeof(mh)))) return PPM_FAILURE_INVALID_USER_MEMORY; /* * data and size */ iov = (const struct iovec __user *)mh.msg_iov; iovcnt = mh.msg_iovlen; res = parse_readv_writev_bufs(args, iov, iovcnt, retval, PRB_FLAG_PUSH_ALL); #ifdef CONFIG_COMPAT } else { if (unlikely(ppm_copy_from_user(&compat_mh, (const void __user *)compat_ptr(val), sizeof(compat_mh)))) return PPM_FAILURE_INVALID_USER_MEMORY; /* * data and size */ compat_iov = (const struct compat_iovec __user *)compat_ptr(compat_mh.msg_iov); iovcnt = compat_mh.msg_iovlen; res = compat_parse_readv_writev_bufs(args, compat_iov, iovcnt, retval, PRB_FLAG_PUSH_ALL); } #endif if (unlikely(res != PPM_SUCCESS)) return res; /* * tuple */ if (retval >= 0) { /* * Get the fd */ if (!args->is_socketcall) { syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); fd = (int)val; } else fd = (int)args->socketcall_args[0]; /* * Get the address */ usrsockaddr = (struct sockaddr __user *)mh.msg_name; addrlen = mh.msg_namelen; if (usrsockaddr != NULL && addrlen != 0) { /* * Copy the address */ err = addr_to_kernel(usrsockaddr, addrlen, (struct sockaddr *)&address); if (likely(err >= 0)) { /* * Convert the fd into socket endpoint information */ size = fd_to_socktuple(fd, (struct sockaddr *)&address, addrlen, true, true, targetbuf, STR_STORAGE_SIZE); } } } /* Copy the endpoint info into the ring */ res = val_to_ring(args, (uint64_t)(unsigned long)targetbuf, size, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_creat_x(struct event_filler_arguments *args) { unsigned long val; unsigned long modes; int res; int64_t retval; /* * fd */ retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * name */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * mode */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &modes); res = val_to_ring(args, open_modes_to_scap(O_CREAT, modes), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * dev */ res = val_to_ring(args, get_fd_dev(retval), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_pipe_x(struct event_filler_arguments *args) { int res; int64_t retval; unsigned long val; int fds[2]; struct file *file; /* * retval */ retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * fds */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); #ifdef CONFIG_COMPAT if (!args->compat) { #endif if (unlikely(ppm_copy_from_user(fds, (const void __user *)val, sizeof(fds)))) return PPM_FAILURE_INVALID_USER_MEMORY; #ifdef CONFIG_COMPAT } else { if (unlikely(ppm_copy_from_user(fds, (const void __user *)compat_ptr(val), sizeof(fds)))) return PPM_FAILURE_INVALID_USER_MEMORY; } #endif res = val_to_ring(args, fds[0], 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; res = val_to_ring(args, fds[1], 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; file = fget(fds[0]); val = 0; if (likely(file != NULL)) { #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) val = file->f_path.dentry->d_inode->i_ino; #else val = file->f_dentry->d_inode->i_ino; #endif fput(file); } res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_eventfd_e(struct event_filler_arguments *args) { int res; unsigned long val; /* * initval */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * flags * XXX not implemented yet */ /* syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); */ val = 0; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_shutdown_e(struct event_filler_arguments *args) { int res; unsigned long val; /* * fd */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); else val = args->socketcall_args[0]; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * how */ if (!args->is_socketcall) syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); else val = args->socketcall_args[1]; res = val_to_ring(args, (unsigned long)shutdown_how_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_futex_e(struct event_filler_arguments *args) { int res; unsigned long val; /* * addr */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * op */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, (unsigned long)futex_op_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * val */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_lseek_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * fd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * offset */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * whence */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); res = val_to_ring(args, lseek_whence_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_llseek_e(struct event_filler_arguments *args) { unsigned long val; int res; unsigned long oh; unsigned long ol; uint64_t offset; /* * fd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * offset * We build it by combining the offset_high and offset_low system call arguments */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &oh); syscall_get_arguments_deprecated(current, args->regs, 2, 1, &ol); offset = (((uint64_t)oh) << 32) + ((uint64_t)ol); res = val_to_ring(args, offset, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * whence */ syscall_get_arguments_deprecated(current, args->regs, 4, 1, &val); res = val_to_ring(args, lseek_whence_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } static int poll_parse_fds(struct event_filler_arguments *args, bool enter_event) { struct pollfd *fds; char *targetbuf; unsigned long val; unsigned long nfds; unsigned long fds_count; u32 j; u32 pos; u16 flags; /* * fds * * Get the number of fds */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &nfds); /* * Check if we have enough space to store both the fd list * from user space and the temporary buffer to serialize to the ring */ if (unlikely(sizeof(struct pollfd) * nfds + 2 + 10 * nfds > STR_STORAGE_SIZE)) return PPM_FAILURE_BUFFER_FULL; /* Get the fds pointer */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); fds = (struct pollfd *)args->str_storage; #ifdef CONFIG_COMPAT if (!args->compat) { #endif if (unlikely(ppm_copy_from_user(fds, (const void __user *)val, nfds * sizeof(struct pollfd)))) return PPM_FAILURE_INVALID_USER_MEMORY; #ifdef CONFIG_COMPAT } else { if (unlikely(ppm_copy_from_user(fds, (const void __user *)compat_ptr(val), nfds * sizeof(struct pollfd)))) return PPM_FAILURE_INVALID_USER_MEMORY; } #endif pos = 2; targetbuf = args->str_storage + nfds * sizeof(struct pollfd) + pos; fds_count = 0; /* Copy each fd into the temporary buffer */ for (j = 0; j < nfds; j++) { if (enter_event) { flags = poll_events_to_scap(fds[j].events); } else { /* * If it's an exit event, we copy only the fds that * returned something */ if (!fds[j].revents) continue; flags = poll_events_to_scap(fds[j].revents); } *(int64_t *)(targetbuf + pos) = fds[j].fd; *(int16_t *)(targetbuf + pos + 8) = flags; pos += 10; ++fds_count; } *(u16 *)(targetbuf) = (u16)fds_count; return val_to_ring(args, (uint64_t)(unsigned long)targetbuf, pos, false, 0); } int f_sys_poll_e(struct event_filler_arguments *args) { unsigned long val; int res; res = poll_parse_fds(args, true); if (unlikely(res != PPM_SUCCESS)) return res; /* * timeout */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } static int timespec_parse(struct event_filler_arguments *args, unsigned long val) { uint64_t longtime; char *targetbuf = args->str_storage; struct timespec *tts = (struct timespec *)targetbuf; #ifdef CONFIG_COMPAT struct compat_timespec *compat_tts = (struct compat_timespec *)targetbuf; #endif int cfulen; /* * interval * We copy the timespec structure and then convert it to a 64bit relative time */ #ifdef CONFIG_COMPAT if (!args->compat) { #endif cfulen = (int)ppm_copy_from_user(targetbuf, (void __user *)val, sizeof(struct timespec)); if (unlikely(cfulen != 0)) return PPM_FAILURE_INVALID_USER_MEMORY; longtime = ((uint64_t)tts->tv_sec) * 1000000000 + tts->tv_nsec; #ifdef CONFIG_COMPAT } else { cfulen = (int)ppm_copy_from_user(targetbuf, (void __user *)compat_ptr(val), sizeof(struct compat_timespec)); if (unlikely(cfulen != 0)) return PPM_FAILURE_INVALID_USER_MEMORY; longtime = ((uint64_t)compat_tts->tv_sec) * 1000000000 + compat_tts->tv_nsec; } #endif return val_to_ring(args, longtime, 0, false, 0); } int f_sys_ppoll_e(struct event_filler_arguments *args) { unsigned long val; int res; res = poll_parse_fds(args, true); if (res != PPM_SUCCESS) return res; /* * timeout */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); /* NULL timeout specified as 0xFFFFFF.... */ if (val == (unsigned long)NULL) res = val_to_ring(args, (uint64_t)(-1), 0, false, 0); else res = timespec_parse(args, val); if (res != PPM_SUCCESS) return res; /* * sigmask */ syscall_get_arguments_deprecated(current, args->regs, 3, 1, &val); if (val != (unsigned long)NULL) if (0 != ppm_copy_from_user(&val, (void __user *)val, sizeof(val))) return PPM_FAILURE_INVALID_USER_MEMORY; res = val_to_ring(args, val, 0, false, 0); if (res != PPM_SUCCESS) return res; return add_sentinel(args); } /* This is the same for poll() and ppoll() */ int f_sys_poll_x(struct event_filler_arguments *args) { int64_t retval; int res; /* * res */ retval = (int64_t)(long)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; res = poll_parse_fds(args, false); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_mount_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * Fix mount flags in arg 3. * See http://lxr.free-electrons.com/source/fs/namespace.c?v=4.2#L2650 */ syscall_get_arguments_deprecated(current, args->regs, 3, 1, &val); if ((val & PPM_MS_MGC_MSK) == PPM_MS_MGC_VAL) val &= ~PPM_MS_MGC_MSK; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_openat_x(struct event_filler_arguments *args) { unsigned long val; unsigned long flags; unsigned long modes; int res; int64_t retval; retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * dirfd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * name */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Flags * Note that we convert them into the ppm portable representation before pushing them to the ring */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &flags); res = val_to_ring(args, open_flags_to_scap(flags), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * mode */ syscall_get_arguments_deprecated(current, args->regs, 3, 1, &modes); res = val_to_ring(args, open_modes_to_scap(flags, modes), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * dev */ res = val_to_ring(args, get_fd_dev(retval), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_unlinkat_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_t retval; retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * dirfd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * name */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * flags * Note that we convert them into the ppm portable representation before pushing them to the ring */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); res = val_to_ring(args, unlinkat_flags_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_linkat_x(struct event_filler_arguments *args) { unsigned long val; unsigned long flags; int res; int64_t retval; retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * olddir */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * oldpath */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * newdir */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * newpath */ syscall_get_arguments_deprecated(current, args->regs, 3, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Flags * Note that we convert them into the ppm portable representation before pushing them to the ring */ syscall_get_arguments_deprecated(current, args->regs, 4, 1, &flags); res = val_to_ring(args, linkat_flags_to_scap(flags), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } #ifndef _64BIT_ARGS_SINGLE_REGISTER int f_sys_pread64_e(struct event_filler_arguments *args) { unsigned long val; unsigned long size; int res; unsigned long pos0; unsigned long pos1; uint64_t pos64; /* * fd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * size */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &size); res = val_to_ring(args, size, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * pos */ #if defined CONFIG_X86 syscall_get_arguments_deprecated(current, args->regs, 3, 1, &pos0); syscall_get_arguments_deprecated(current, args->regs, 4, 1, &pos1); #elif defined CONFIG_ARM && CONFIG_AEABI syscall_get_arguments_deprecated(current, args->regs, 4, 1, &pos0); syscall_get_arguments_deprecated(current, args->regs, 5, 1, &pos1); #else #error This architecture/abi not yet supported #endif pos64 = merge_64(pos1, pos0); res = val_to_ring(args, pos64, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } #endif /* _64BIT_ARGS_SINGLE_REGISTER */ #ifndef _64BIT_ARGS_SINGLE_REGISTER int f_sys_pwrite64_e(struct event_filler_arguments *args) { unsigned long val; unsigned long size; int res; #ifndef _64BIT_ARGS_SINGLE_REGISTER unsigned long pos0; unsigned long pos1; uint64_t pos64; #endif /* * fd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * size */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &size); res = val_to_ring(args, size, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * pos * NOTE: this is a 64bit value, which means that on 32bit systems it uses two * separate registers that we need to merge. */ #ifdef _64BIT_ARGS_SINGLE_REGISTER syscall_get_arguments_deprecated(current, args->regs, 3, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; #else #if defined CONFIG_X86 syscall_get_arguments_deprecated(current, args->regs, 3, 1, &pos0); syscall_get_arguments_deprecated(current, args->regs, 4, 1, &pos1); #elif defined CONFIG_ARM && CONFIG_AEABI syscall_get_arguments_deprecated(current, args->regs, 4, 1, &pos0); syscall_get_arguments_deprecated(current, args->regs, 5, 1, &pos1); #else #error This architecture/abi not yet supported #endif pos64 = merge_64(pos1, pos0); res = val_to_ring(args, pos64, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; #endif return add_sentinel(args); } #endif int f_sys_readv_preadv_x(struct event_filler_arguments *args) { unsigned long val; int64_t retval; int res; #ifdef CONFIG_COMPAT const struct compat_iovec __user *compat_iov; #endif const struct iovec __user *iov; unsigned long iovcnt; /* * res */ retval = (int64_t)(long)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * data and size */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); syscall_get_arguments_deprecated(current, args->regs, 2, 1, &iovcnt); #ifdef CONFIG_COMPAT if (unlikely(args->compat)) { compat_iov = (const struct compat_iovec __user *)compat_ptr(val); res = compat_parse_readv_writev_bufs(args, compat_iov, iovcnt, retval, PRB_FLAG_PUSH_ALL); } else #endif { iov = (const struct iovec __user *)val; res = parse_readv_writev_bufs(args, iov, iovcnt, retval, PRB_FLAG_PUSH_ALL); } if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_writev_e(struct event_filler_arguments *args) { unsigned long val; int res; #ifdef CONFIG_COMPAT const struct compat_iovec __user *compat_iov; #endif const struct iovec __user *iov; unsigned long iovcnt; /* * fd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * size */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &iovcnt); /* * Copy the buffer */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); #ifdef CONFIG_COMPAT if (unlikely(args->compat)) { compat_iov = (const struct compat_iovec __user *)compat_ptr(val); res = compat_parse_readv_writev_bufs(args, compat_iov, iovcnt, args->consumer->snaplen, PRB_FLAG_PUSH_SIZE | PRB_FLAG_IS_WRITE); } else #endif { iov = (const struct iovec __user *)val; res = parse_readv_writev_bufs(args, iov, iovcnt, args->consumer->snaplen, PRB_FLAG_PUSH_SIZE | PRB_FLAG_IS_WRITE); } if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_writev_pwritev_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_t retval; #ifdef CONFIG_COMPAT const struct compat_iovec __user *compat_iov; #endif const struct iovec __user *iov; unsigned long iovcnt; /* * res */ retval = (int64_t)(long)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * data and size */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &iovcnt); /* * Copy the buffer */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); #ifdef CONFIG_COMPAT if (unlikely(args->compat)) { compat_iov = (const struct compat_iovec __user *)compat_ptr(val); res = compat_parse_readv_writev_bufs(args, compat_iov, iovcnt, args->consumer->snaplen, PRB_FLAG_PUSH_DATA | PRB_FLAG_IS_WRITE); } else #endif { iov = (const struct iovec __user *)val; res = parse_readv_writev_bufs(args, iov, iovcnt, args->consumer->snaplen, PRB_FLAG_PUSH_DATA | PRB_FLAG_IS_WRITE); } if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } #ifndef _64BIT_ARGS_SINGLE_REGISTER int f_sys_preadv64_e(struct event_filler_arguments *args) { unsigned long val; int res; unsigned long pos0; unsigned long pos1; uint64_t pos64; /* * fd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * pos */ /* * Note that in preadv and pwritev have NO 64-bit arguments in the * syscall (despite having one in the userspace API), so no alignment * requirements apply here. For an overly-detailed discussion about * this, see https://lwn.net/Articles/311630/ */ syscall_get_arguments_deprecated(current, args->regs, 3, 1, &pos0); syscall_get_arguments_deprecated(current, args->regs, 4, 1, &pos1); pos64 = merge_64(pos1, pos0); res = val_to_ring(args, pos64, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } #endif /* _64BIT_ARGS_SINGLE_REGISTER */ int f_sys_pwritev_e(struct event_filler_arguments *args) { unsigned long val; int res; #ifndef _64BIT_ARGS_SINGLE_REGISTER unsigned long pos0; unsigned long pos1; uint64_t pos64; #endif #ifdef CONFIG_COMPAT const struct compat_iovec __user *compat_iov; #endif const struct iovec __user *iov; unsigned long iovcnt; /* * fd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * size */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &iovcnt); /* * Copy the buffer */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); #ifdef CONFIG_COMPAT if (unlikely(args->compat)) { compat_iov = (const struct compat_iovec __user *)compat_ptr(val); res = compat_parse_readv_writev_bufs(args, compat_iov, iovcnt, args->consumer->snaplen, PRB_FLAG_PUSH_SIZE | PRB_FLAG_IS_WRITE); } else #endif { iov = (const struct iovec __user *)val; res = parse_readv_writev_bufs(args, iov, iovcnt, args->consumer->snaplen, PRB_FLAG_PUSH_SIZE | PRB_FLAG_IS_WRITE); } if (unlikely(res != PPM_SUCCESS)) return res; /* * pos * NOTE: this is a 64bit value, which means that on 32bit systems it uses two * separate registers that we need to merge. */ #ifdef _64BIT_ARGS_SINGLE_REGISTER syscall_get_arguments_deprecated(current, args->regs, 3, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; #else /* * Note that in preadv and pwritev have NO 64-bit arguments in the * syscall (despite having one in the userspace API), so no alignment * requirements apply here. For an overly-detailed discussion about * this, see https://lwn.net/Articles/311630/ */ syscall_get_arguments_deprecated(current, args->regs, 3, 1, &pos0); syscall_get_arguments_deprecated(current, args->regs, 4, 1, &pos1); pos64 = merge_64(pos1, pos0); res = val_to_ring(args, pos64, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; #endif return add_sentinel(args); } int f_sys_nanosleep_e(struct event_filler_arguments *args) { unsigned long val; int res; syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = timespec_parse(args, val); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_getrlimit_setrlimit_e(struct event_filler_arguments *args) { u8 ppm_resource; unsigned long val; int res; /* * resource */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); ppm_resource = rlimit_resource_to_scap(val); res = val_to_ring(args, (uint64_t)ppm_resource, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_getrlimit_setrlrimit_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_t retval; struct rlimit rl; #ifdef CONFIG_COMPAT struct compat_rlimit compat_rl; #endif int64_t cur; int64_t max; /* * res */ retval = (int64_t)(long)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Copy the user structure and extract cur and max */ if (retval >= 0 || args->event_type == PPME_SYSCALL_SETRLIMIT_X) { syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); #ifdef CONFIG_COMPAT if (!args->compat) { #endif if (unlikely(ppm_copy_from_user(&rl, (const void __user *)val, sizeof(struct rlimit)))) return PPM_FAILURE_INVALID_USER_MEMORY; cur = rl.rlim_cur; max = rl.rlim_max; #ifdef CONFIG_COMPAT } else { if (unlikely(ppm_copy_from_user(&compat_rl, (const void __user *)compat_ptr(val), sizeof(struct compat_rlimit)))) return PPM_FAILURE_INVALID_USER_MEMORY; cur = compat_rl.rlim_cur; max = compat_rl.rlim_max; } #endif } else { cur = -1; max = -1; } /* * cur */ res = val_to_ring(args, cur, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * max */ res = val_to_ring(args, max, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_prlimit_e(struct event_filler_arguments *args) { u8 ppm_resource; unsigned long val; int res; /* * pid */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * resource */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); ppm_resource = rlimit_resource_to_scap(val); res = val_to_ring(args, (uint64_t)ppm_resource, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_prlimit_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_t retval; struct rlimit rl; #ifdef CONFIG_COMPAT struct compat_rlimit compat_rl; #endif int64_t newcur; int64_t newmax; int64_t oldcur; int64_t oldmax; /* * res */ retval = (int64_t)(long)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Copy the user structure and extract cur and max */ if (retval >= 0) { syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); #ifdef CONFIG_COMPAT if (!args->compat) { #endif if (unlikely(ppm_copy_from_user(&rl, (const void __user *)val, sizeof(struct rlimit)))) { newcur = -1; newmax = -1; } else { newcur = rl.rlim_cur; newmax = rl.rlim_max; } #ifdef CONFIG_COMPAT } else { if (unlikely(ppm_copy_from_user(&compat_rl, (const void __user *)val, sizeof(struct compat_rlimit)))) { newcur = -1; newmax = -1; } else { newcur = compat_rl.rlim_cur; newmax = compat_rl.rlim_max; } } #endif } else { newcur = -1; newmax = -1; } syscall_get_arguments_deprecated(current, args->regs, 3, 1, &val); #ifdef CONFIG_COMPAT if (!args->compat) { #endif if (unlikely(ppm_copy_from_user(&rl, (const void __user *)val, sizeof(struct rlimit)))) { oldcur = -1; oldmax = -1; } else { oldcur = rl.rlim_cur; oldmax = rl.rlim_max; } #ifdef CONFIG_COMPAT } else { if (unlikely(ppm_copy_from_user(&compat_rl, (const void __user *)val, sizeof(struct compat_rlimit)))) { oldcur = -1; oldmax = -1; } else { oldcur = compat_rl.rlim_cur; oldmax = compat_rl.rlim_max; } } #endif /* * newcur */ res = val_to_ring(args, newcur, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * newmax */ res = val_to_ring(args, newmax, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * oldcur */ res = val_to_ring(args, oldcur, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * oldmax */ res = val_to_ring(args, oldmax, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } #ifdef CAPTURE_CONTEXT_SWITCHES int f_sched_switch_e(struct event_filler_arguments *args) { int res; long total_vm = 0; long total_rss = 0; long swap = 0; struct mm_struct *mm = NULL; if (args->sched_prev == NULL || args->sched_next == NULL) { ASSERT(false); return -1; } /* * next */ res = val_to_ring(args, args->sched_next->pid, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * pgft_maj */ res = val_to_ring(args, args->sched_prev->maj_flt, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * pgft_min */ res = val_to_ring(args, args->sched_prev->min_flt, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; mm = args->sched_prev->mm; if (mm) { total_vm = mm->total_vm << (PAGE_SHIFT-10); total_rss = ppm_get_mm_rss(mm) << (PAGE_SHIFT-10); swap = ppm_get_mm_swap(mm) << (PAGE_SHIFT-10); } /* * vm_size */ res = val_to_ring(args, total_vm, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * vm_rss */ res = val_to_ring(args, total_rss, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * vm_swap */ res = val_to_ring(args, swap, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; #if 0 /* * steal */ steal = cputime64_to_clock_t(kcpustat_this_cpu->cpustat[CPUTIME_STEAL]); res = val_to_ring(args, steal, 0, false); if (unlikely(res != PPM_SUCCESS)) return res; #endif return add_sentinel(args); } #endif /* CAPTURE_CONTEXT_SWITCHES */ int f_sched_drop(struct event_filler_arguments *args) { int res; /* * ratio */ res = val_to_ring(args, args->consumer->sampling_ratio, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_fcntl_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * fd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * cmd */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, fcntl_cmd_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } static inline int parse_ptrace_addr(struct event_filler_arguments *args, u16 request) { unsigned long val; uint64_t dst; u8 idx; syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); switch (request) { default: idx = PPM_PTRACE_IDX_UINT64; dst = (uint64_t)val; } return val_to_ring(args, dst, 0, false, idx); } static inline int parse_ptrace_data(struct event_filler_arguments *args, u16 request) { unsigned long val; unsigned long len; uint64_t dst; u8 idx; syscall_get_arguments_deprecated(current, args->regs, 3, 1, &val); switch (request) { case PPM_PTRACE_PEEKTEXT: case PPM_PTRACE_PEEKDATA: case PPM_PTRACE_PEEKUSR: idx = PPM_PTRACE_IDX_UINT64; #ifdef CONFIG_COMPAT if (!args->compat) { #endif len = ppm_copy_from_user(&dst, (const void __user *)val, sizeof(long)); #ifdef CONFIG_COMPAT } else { len = ppm_copy_from_user(&dst, (const void __user *)compat_ptr(val), sizeof(compat_long_t)); } #endif if (unlikely(len != 0)) return PPM_FAILURE_INVALID_USER_MEMORY; break; case PPM_PTRACE_CONT: case PPM_PTRACE_SINGLESTEP: case PPM_PTRACE_DETACH: case PPM_PTRACE_SYSCALL: idx = PPM_PTRACE_IDX_SIGTYPE; dst = (uint64_t)val; break; case PPM_PTRACE_ATTACH: case PPM_PTRACE_TRACEME: case PPM_PTRACE_POKETEXT: case PPM_PTRACE_POKEDATA: case PPM_PTRACE_POKEUSR: default: idx = PPM_PTRACE_IDX_UINT64; dst = (uint64_t)val; break; } return val_to_ring(args, dst, 0, false, idx); } int f_sys_ptrace_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * request */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, ptrace_requests_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * pid */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_ptrace_x(struct event_filler_arguments *args) { unsigned long val; int64_t retval; u16 request; int res; /* * res */ retval = (int64_t)(long)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; if (retval < 0) { res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } /* * request */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); request = ptrace_requests_to_scap(val); res = parse_ptrace_addr(args, request); if (unlikely(res != PPM_SUCCESS)) return res; res = parse_ptrace_data(args, request); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_brk_munmap_mmap_x(struct event_filler_arguments *args) { int64_t retval; int res = 0; struct mm_struct *mm = current->mm; long total_vm = 0; long total_rss = 0; long swap = 0; retval = (int64_t)(long)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; if (mm) { total_vm = mm->total_vm << (PAGE_SHIFT-10); total_rss = ppm_get_mm_rss(mm) << (PAGE_SHIFT-10); swap = ppm_get_mm_swap(mm) << (PAGE_SHIFT-10); } /* * vm_size */ res = val_to_ring(args, total_vm, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * vm_rss */ res = val_to_ring(args, total_rss, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * vm_swap */ res = val_to_ring(args, swap, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_mmap_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * addr */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * length */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * prot */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); res = val_to_ring(args, prot_flags_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * flags */ syscall_get_arguments_deprecated(current, args->regs, 3, 1, &val); res = val_to_ring(args, mmap_flags_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * fd */ syscall_get_arguments_deprecated(current, args->regs, 4, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * offset/pgoffset */ syscall_get_arguments_deprecated(current, args->regs, 5, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_renameat_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_t retval; retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * olddirfd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * oldpath */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * newdirfd */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * newpath */ syscall_get_arguments_deprecated(current, args->regs, 3, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_symlinkat_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_t retval; retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * oldpath */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * newdirfd */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * newpath */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_procexit_e(struct event_filler_arguments *args) { int res; if (args->sched_prev == NULL) { ASSERT(false); return -1; } /* * status */ res = val_to_ring(args, args->sched_prev->exit_code, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_sendfile_e(struct event_filler_arguments *args) { unsigned long val; int res; off_t offset; /* * out_fd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * in_fd */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * offset */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); if (val != 0) { #ifdef CONFIG_COMPAT if (!args->compat) { #endif res = ppm_copy_from_user(&offset, (void *)val, sizeof(off_t)); #ifdef CONFIG_COMPAT } else { res = ppm_copy_from_user(&offset, (void *)compat_ptr(val), sizeof(compat_off_t)); } #endif if (unlikely(res)) val = 0; else val = offset; } res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * size */ syscall_get_arguments_deprecated(current, args->regs, 3, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_sendfile_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_t retval; off_t offset; /* * res */ retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * offset */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); if (val != 0) { #ifdef CONFIG_COMPAT if (!args->compat) { #endif res = ppm_copy_from_user(&offset, (void *)val, sizeof(off_t)); #ifdef CONFIG_COMPAT } else { res = ppm_copy_from_user(&offset, (void *)compat_ptr(val), sizeof(compat_off_t)); } #endif if (unlikely(res)) val = 0; else val = offset; } res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_quotactl_e(struct event_filler_arguments *args) { unsigned long val; int res; uint32_t id; uint8_t quota_fmt; uint16_t cmd; /* * extract cmd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); cmd = quotactl_cmd_to_scap(val); res = val_to_ring(args, cmd, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * extract type */ res = val_to_ring(args, quotactl_type_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * extract id */ id = 0; syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); if ((cmd == PPM_Q_GETQUOTA) || (cmd == PPM_Q_SETQUOTA) || (cmd == PPM_Q_XGETQUOTA) || (cmd == PPM_Q_XSETQLIM)) { /* * in this case id represent an userid or groupid so add it */ id = val; } res = val_to_ring(args, id, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * extract quota_fmt from id */ quota_fmt = PPM_QFMT_NOT_USED; if (cmd == PPM_Q_QUOTAON) quota_fmt = quotactl_fmt_to_scap(val); res = val_to_ring(args, quota_fmt, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_quotactl_x(struct event_filler_arguments *args) { unsigned long val, len; int res; int64_t retval; uint16_t cmd; struct if_dqblk dqblk; struct if_dqinfo dqinfo; uint32_t quota_fmt_out; const char empty_string[] = ""; /* * extract cmd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); cmd = quotactl_cmd_to_scap(val); /* * return value */ retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Add special */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * get addr */ syscall_get_arguments_deprecated(current, args->regs, 3, 1, &val); /* * get quotafilepath only for QUOTAON */ if (cmd == PPM_Q_QUOTAON) res = val_to_ring(args, val, 0, true, 0); else res = val_to_ring(args, (unsigned long)empty_string, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * dqblk fields if present */ dqblk.dqb_valid = 0; if ((cmd == PPM_Q_GETQUOTA) || (cmd == PPM_Q_SETQUOTA)) { len = ppm_copy_from_user(&dqblk, (void *)val, sizeof(struct if_dqblk)); if (unlikely(len != 0)) return PPM_FAILURE_INVALID_USER_MEMORY; } if (dqblk.dqb_valid & QIF_BLIMITS) { res = val_to_ring(args, dqblk.dqb_bhardlimit, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; res = val_to_ring(args, dqblk.dqb_bsoftlimit, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } else { res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } if (dqblk.dqb_valid & QIF_SPACE) { res = val_to_ring(args, dqblk.dqb_curspace, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } else { res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } if (dqblk.dqb_valid & QIF_ILIMITS) { res = val_to_ring(args, dqblk.dqb_ihardlimit, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; res = val_to_ring(args, dqblk.dqb_isoftlimit, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } else { res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } if (dqblk.dqb_valid & QIF_BTIME) { res = val_to_ring(args, dqblk.dqb_btime, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } else { res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } if (dqblk.dqb_valid & QIF_ITIME) { res = val_to_ring(args, dqblk.dqb_itime, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } else { res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } /* * dqinfo fields if present */ dqinfo.dqi_valid = 0; if ((cmd == PPM_Q_GETINFO) || (cmd == PPM_Q_SETINFO)) { len = ppm_copy_from_user(&dqinfo, (void *)val, sizeof(struct if_dqinfo)); if (unlikely(len != 0)) return PPM_FAILURE_INVALID_USER_MEMORY; } if (dqinfo.dqi_valid & IIF_BGRACE) { res = val_to_ring(args, dqinfo.dqi_bgrace, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } else { res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } if (dqinfo.dqi_valid & IIF_IGRACE) { res = val_to_ring(args, dqinfo.dqi_igrace, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } else { res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } if (dqinfo.dqi_valid & IIF_FLAGS) { res = val_to_ring(args, dqinfo.dqi_flags, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } else { res = val_to_ring(args, 0, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } quota_fmt_out = PPM_QFMT_NOT_USED; if (cmd == PPM_Q_GETFMT) { len = ppm_copy_from_user("a_fmt_out, (void *)val, sizeof(uint32_t)); if (unlikely(len != 0)) return PPM_FAILURE_INVALID_USER_MEMORY; quota_fmt_out = quotactl_fmt_to_scap(quota_fmt_out); } res = val_to_ring(args, quota_fmt_out, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_sysdigevent_e(struct event_filler_arguments *args) { int res; /* * event_type */ res = val_to_ring(args, (unsigned long)args->sched_prev, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * event_data */ res = val_to_ring(args, (unsigned long)args->sched_next, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_getresuid_and_gid_x(struct event_filler_arguments *args) { int res; unsigned long val, len; uint32_t uid; int16_t retval; /* * return value */ retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * ruid */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); #ifdef CONFIG_COMPAT if (!args->compat) { #endif len = ppm_copy_from_user(&uid, (void *)val, sizeof(uint32_t)); #ifdef CONFIG_COMPAT } else { len = ppm_copy_from_user(&uid, (void *)compat_ptr(val), sizeof(uint32_t)); } #endif if (unlikely(len != 0)) return PPM_FAILURE_INVALID_USER_MEMORY; res = val_to_ring(args, uid, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * euid */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); len = ppm_copy_from_user(&uid, (void *)val, sizeof(uint32_t)); if (unlikely(len != 0)) return PPM_FAILURE_INVALID_USER_MEMORY; res = val_to_ring(args, uid, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * suid */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); len = ppm_copy_from_user(&uid, (void *)val, sizeof(uint32_t)); if (unlikely(len != 0)) return PPM_FAILURE_INVALID_USER_MEMORY; res = val_to_ring(args, uid, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_flock_e(struct event_filler_arguments *args) { unsigned long val; int res; u32 flags; syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); flags = flock_flags_to_scap(val); res = val_to_ring(args, flags, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_setns_e(struct event_filler_arguments *args) { unsigned long val; int res; u32 flags; /* * parse fd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * get type, parse as clone flags as it's a subset of it */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); flags = clone_flags_to_scap(val); res = val_to_ring(args, flags, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_unshare_e(struct event_filler_arguments *args) { unsigned long val; int res; u32 flags; /* * get type, parse as clone flags as it's a subset of it */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); flags = clone_flags_to_scap(val); res = val_to_ring(args, flags, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } #ifdef CAPTURE_SIGNAL_DELIVERIES int f_sys_signaldeliver_e(struct event_filler_arguments *args) { int res; /* * source pid */ res = val_to_ring(args, args->spid, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * destination pid */ res = val_to_ring(args, args->dpid, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * signal number */ res = val_to_ring(args, args->signo, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } #endif #ifdef CAPTURE_PAGE_FAULTS int f_sys_pagefault_e(struct event_filler_arguments *args) { int res; res = val_to_ring(args, args->fault_data.address, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; res = val_to_ring(args, args->fault_data.regs->ip, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; res = val_to_ring(args, pf_flags_to_scap(args->fault_data.error_code), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } #endif int f_cpu_hotplug_e(struct event_filler_arguments *args) { int res; /* * cpu */ res = val_to_ring(args, (uint64_t)args->sched_prev, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * action */ res = val_to_ring(args, (uint64_t)args->sched_next, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_semop_x(struct event_filler_arguments *args) { unsigned long nsops; int res; int64_t retval; struct sembuf *ptr; /* * return value */ retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * nsops * actually this could be read in the enter function but * we also need to know the value to access the sembuf structs */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &nsops); res = val_to_ring(args, nsops, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * sembuf */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, (unsigned long *) &ptr); if (nsops && ptr) { /* max length of sembuf array in g_event_info = 2 */ const unsigned max_nsops = 2; unsigned j; for (j = 0; j < max_nsops; j++) { struct sembuf sops = {0, 0, 0}; if (nsops--) if (unlikely(ppm_copy_from_user(&sops, (void *)&ptr[j], sizeof(struct sembuf)))) return PPM_FAILURE_INVALID_USER_MEMORY; res = val_to_ring(args, sops.sem_num, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; res = val_to_ring(args, sops.sem_op, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; res = val_to_ring(args, semop_flags_to_scap(sops.sem_flg), 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; } } return add_sentinel(args); } int f_sys_semget_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * key */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * nsems */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * semflg */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); res = val_to_ring(args, semget_flags_to_scap(val), 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_semctl_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * semid */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * semnum */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * cmd */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); res = val_to_ring(args, semctl_cmd_to_scap(val), 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * optional argument semun/val */ if (val == SETVAL) syscall_get_arguments_deprecated(current, args->regs, 3, 1, &val); else val = 0; res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_access_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * mode */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, access_flags_to_scap(val), 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_bpf_x(struct event_filler_arguments *args) { int64_t retval; unsigned long cmd; int res; /* * res, if failure or depending on cmd */ retval = (int64_t)(long)syscall_get_return_value(current, args->regs); if (retval < 0) { res = val_to_ring(args, retval, 0, false, PPM_BPF_IDX_RES); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } /* * fd, depending on cmd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &cmd); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) if(cmd == BPF_MAP_CREATE || cmd == BPF_PROG_LOAD) #else if(0) #endif { res = val_to_ring(args, retval, 0, false, PPM_BPF_IDX_FD); } else { res = val_to_ring(args, retval, 0, false, PPM_BPF_IDX_RES); } if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_mkdirat_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_t retval; retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * dirfd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * path */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * mode */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_fchmodat_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_t retval; retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * dirfd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); if ((int)val == AT_FDCWD) val = PPM_AT_FDCWD; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * filename */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * mode */ syscall_get_arguments_deprecated(current, args->regs, 2, 1, &val); res = val_to_ring(args, chmod_mode_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_chmod_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_t retval; retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * filename */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, true, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * mode */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, chmod_mode_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } int f_sys_fchmod_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_t retval; retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * fd */ syscall_get_arguments_deprecated(current, args->regs, 0, 1, &val); res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * mode */ syscall_get_arguments_deprecated(current, args->regs, 1, 1, &val); res = val_to_ring(args, chmod_mode_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } sysdig-0.26.4/driver/ppm_fillers.h000066400000000000000000000063141352731327100170750ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifndef PPM_FILLERS_H_ #define PPM_FILLERS_H_ /* This is described in syscall(2). Some syscalls take 64-bit arguments. On * arches that have 64-bit registers, these arguments are shipped in a register. * On 32-bit arches, however, these are split between two consecutive registers, * with some alignment requirements. Some require an odd/even pair while some * others require even/odd. For now I assume they all do what x86_32 does, and * we can handle the rest when we port those. */ #ifdef __KERNEL__ #ifdef CONFIG_64BIT #define _64BIT_ARGS_SINGLE_REGISTER #endif /* CONFIG_64BIT */ #else #ifdef __x86_64__ #define _64BIT_ARGS_SINGLE_REGISTER #endif /* __x86_64__ */ #endif /* __KERNEL__ */ #define FILLER_LIST_MAPPER(FN) \ FN(sys_autofill) \ FN(sys_generic) \ FN(sys_empty) \ FN(sys_single) \ FN(sys_single_x) \ FN(sys_open_x) \ FN(sys_read_x) \ FN(sys_write_x) \ FN(sys_execve_e) \ FN(proc_startupdate) \ FN(proc_startupdate_2) \ FN(proc_startupdate_3) \ FN(sys_socketpair_x) \ FN(sys_setsockopt_x) \ FN(sys_getsockopt_x) \ FN(sys_connect_x) \ FN(sys_accept4_e) \ FN(sys_accept_x) \ FN(sys_send_e) \ FN(sys_send_x) \ FN(sys_sendto_e) \ FN(sys_sendmsg_e) \ FN(sys_sendmsg_x) \ FN(sys_recv_x) \ FN(sys_recvfrom_x) \ FN(sys_recvmsg_x) \ FN(sys_recvmsg_x_2) \ FN(sys_shutdown_e) \ FN(sys_creat_x) \ FN(sys_pipe_x) \ FN(sys_eventfd_e) \ FN(sys_futex_e) \ FN(sys_lseek_e) \ FN(sys_llseek_e) \ FN(sys_socket_bind_x) \ FN(sys_poll_e) \ FN(sys_poll_x) \ FN(sys_pread64_e) \ FN(sys_preadv64_e) \ FN(sys_writev_e) \ FN(sys_pwrite64_e) \ FN(sys_readv_preadv_x) \ FN(sys_writev_pwritev_x) \ FN(sys_pwritev_e) \ FN(sys_nanosleep_e) \ FN(sys_getrlimit_setrlimit_e) \ FN(sys_getrlimit_setrlrimit_x) \ FN(sys_prlimit_e) \ FN(sys_prlimit_x) \ FN(sched_switch_e) \ FN(sched_drop) \ FN(sys_fcntl_e) \ FN(sys_ptrace_e) \ FN(sys_ptrace_x) \ FN(sys_mmap_e) \ FN(sys_brk_munmap_mmap_x) \ FN(sys_renameat_x) \ FN(sys_symlinkat_x) \ FN(sys_procexit_e) \ FN(sys_sendfile_e) \ FN(sys_sendfile_x) \ FN(sys_quotactl_e) \ FN(sys_quotactl_x) \ FN(sys_sysdigevent_e) \ FN(sys_getresuid_and_gid_x) \ FN(sys_signaldeliver_e) \ FN(sys_pagefault_e) \ FN(sys_setns_e) \ FN(sys_unshare_e) \ FN(sys_flock_e) \ FN(cpu_hotplug_e) \ FN(sys_semop_x) \ FN(sys_semget_e) \ FN(sys_semctl_e) \ FN(sys_ppoll_e) \ FN(sys_mount_e) \ FN(sys_access_e) \ FN(sys_socket_x) \ FN(sys_bpf_x) \ FN(sys_unlinkat_x) \ FN(sys_fchmodat_x) \ FN(sys_chmod_x) \ FN(sys_fchmod_x) \ FN(sys_mkdirat_x) \ FN(sys_openat_x) \ FN(sys_linkat_x) \ FN(terminate_filler) #define FILLER_ENUM_FN(x) PPM_FILLER_##x, enum ppm_filler_id { FILLER_LIST_MAPPER(FILLER_ENUM_FN) PPM_FILLER_MAX }; #undef FILLER_ENUM_FN #define FILLER_PROTOTYPE_FN(x) int f_##x(struct event_filler_arguments *args); FILLER_LIST_MAPPER(FILLER_PROTOTYPE_FN) #undef FILLER_PROTOTYPE_FN #endif /* PPM_FILLERS_H_ */ sysdig-0.26.4/driver/ppm_flag_helpers.h000066400000000000000000000630401352731327100200670ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifndef PPM_FLAG_HELPERS_H_ #define PPM_FLAG_HELPERS_H_ #include #include #include #include "ppm.h" #define PPM_MS_MGC_MSK 0xffff0000 #define PPM_MS_MGC_VAL 0xC0ED0000 static __always_inline uint32_t open_flags_to_scap(unsigned long flags) { uint32_t res = 0; switch (flags & (O_RDONLY | O_WRONLY | O_RDWR)) { case O_WRONLY: res |= PPM_O_WRONLY; break; case O_RDWR: res |= PPM_O_RDWR; break; default: res |= PPM_O_RDONLY; break; } if (flags & O_CREAT) res |= PPM_O_CREAT; if (flags & O_APPEND) res |= PPM_O_APPEND; #ifdef O_DSYNC if (flags & O_DSYNC) res |= PPM_O_DSYNC; #endif if (flags & O_EXCL) res |= PPM_O_EXCL; if (flags & O_NONBLOCK) res |= PPM_O_NONBLOCK; if (flags & O_SYNC) res |= PPM_O_SYNC; if (flags & O_TRUNC) res |= PPM_O_TRUNC; #ifdef O_DIRECT if (flags & O_DIRECT) res |= PPM_O_DIRECT; #endif #ifdef O_DIRECTORY if (flags & O_DIRECTORY) res |= PPM_O_DIRECTORY; #endif #ifdef O_LARGEFILE if (flags & O_LARGEFILE) res |= PPM_O_LARGEFILE; #endif #ifdef O_CLOEXEC if (flags & O_CLOEXEC) res |= PPM_O_CLOEXEC; #endif return res; } static __always_inline u32 open_modes_to_scap(unsigned long flags, unsigned long modes) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) unsigned long flags_mask = O_CREAT | O_TMPFILE; #else unsigned long flags_mask = O_CREAT; #endif u32 res = 0; if ((flags & flags_mask) == 0) return res; if (modes & S_IRUSR) res |= PPM_S_IRUSR; if (modes & S_IWUSR) res |= PPM_S_IWUSR; if (modes & S_IXUSR) res |= PPM_S_IXUSR; /* * PPM_S_IRWXU == S_IRUSR | S_IWUSR | S_IXUSR */ if (modes & S_IRGRP) res |= PPM_S_IRGRP; if (modes & S_IWGRP) res |= PPM_S_IWGRP; if (modes & S_IXGRP) res |= PPM_S_IXGRP; /* * PPM_S_IRWXG == S_IRGRP | S_IWGRP | S_IXGRP */ if (modes & S_IROTH) res |= PPM_S_IROTH; if (modes & S_IWOTH) res |= PPM_S_IWOTH; if (modes & S_IXOTH) res |= PPM_S_IXOTH; /* * PPM_S_IRWXO == S_IROTH | S_IWOTH | S_IXOTH */ if (modes & S_ISUID) res |= PPM_S_ISUID; if (modes & S_ISGID) res |= PPM_S_ISGID; if (modes & S_ISVTX) res |= PPM_S_ISVTX; return res; } static __always_inline u32 clone_flags_to_scap(unsigned long flags) { u32 res = 0; if (flags & CLONE_FILES) res |= PPM_CL_CLONE_FILES; if (flags & CLONE_FS) res |= PPM_CL_CLONE_FS; #ifdef CLONE_IO if (flags & CLONE_IO) res |= PPM_CL_CLONE_IO; #endif #ifdef CLONE_NEWIPC if (flags & CLONE_NEWIPC) res |= PPM_CL_CLONE_NEWIPC; #endif #ifdef CLONE_NEWNET if (flags & CLONE_NEWNET) res |= PPM_CL_CLONE_NEWNET; #endif #ifdef CLONE_NEWNS if (flags & CLONE_NEWNS) res |= PPM_CL_CLONE_NEWNS; #endif #ifdef CLONE_NEWPID if (flags & CLONE_NEWPID) res |= PPM_CL_CLONE_NEWPID; #endif #ifdef CLONE_NEWUTS if (flags & CLONE_NEWUTS) res |= PPM_CL_CLONE_NEWUTS; #endif if (flags & CLONE_PARENT_SETTID) res |= PPM_CL_CLONE_PARENT_SETTID; if (flags & CLONE_PARENT) res |= PPM_CL_CLONE_PARENT; if (flags & CLONE_PTRACE) res |= PPM_CL_CLONE_PTRACE; if (flags & CLONE_SIGHAND) res |= PPM_CL_CLONE_SIGHAND; if (flags & CLONE_SYSVSEM) res |= PPM_CL_CLONE_SYSVSEM; if (flags & CLONE_THREAD) res |= PPM_CL_CLONE_THREAD; if (flags & CLONE_UNTRACED) res |= PPM_CL_CLONE_UNTRACED; if (flags & CLONE_VM) res |= PPM_CL_CLONE_VM; #ifdef CLONE_NEWUSER if (flags & CLONE_NEWUSER) res |= PPM_CL_CLONE_NEWUSER; #endif if (flags & CLONE_CHILD_CLEARTID) res |= PPM_CL_CLONE_CHILD_CLEARTID; if (flags & CLONE_CHILD_SETTID) res |= PPM_CL_CLONE_CHILD_SETTID; if (flags & CLONE_SETTLS) res |= PPM_CL_CLONE_SETTLS; #ifdef CLONE_STOPPED if (flags & CLONE_STOPPED) res |= PPM_CL_CLONE_STOPPED; #endif if (flags & CLONE_VFORK) res |= PPM_CL_CLONE_VFORK; #ifdef CLONE_NEWCGROUP if (flags & CLONE_NEWCGROUP) res |= PPM_CL_CLONE_NEWCGROUP; #endif return res; } static __always_inline u8 socket_family_to_scap(u8 family) { if (family == AF_INET) return PPM_AF_INET; else if (family == AF_INET6) return PPM_AF_INET6; else if (family == AF_UNIX) return PPM_AF_UNIX; else if (family == AF_NETLINK) return PPM_AF_NETLINK; else if (family == AF_PACKET) return PPM_AF_PACKET; else if (family == AF_UNSPEC) return PPM_AF_UNSPEC; else if (family == AF_AX25) return PPM_AF_AX25; else if (family == AF_IPX) return PPM_AF_IPX; else if (family == AF_APPLETALK) return PPM_AF_APPLETALK; else if (family == AF_NETROM) return PPM_AF_NETROM; else if (family == AF_BRIDGE) return PPM_AF_BRIDGE; else if (family == AF_ATMPVC) return PPM_AF_ATMPVC; else if (family == AF_X25) return PPM_AF_X25; else if (family == AF_ROSE) return PPM_AF_ROSE; else if (family == AF_DECnet) return PPM_AF_DECnet; else if (family == AF_NETBEUI) return PPM_AF_NETBEUI; else if (family == AF_SECURITY) return PPM_AF_SECURITY; else if (family == AF_KEY) return PPM_AF_KEY; else if (family == AF_ROUTE) return PPM_AF_ROUTE; else if (family == AF_ASH) return PPM_AF_ASH; else if (family == AF_ECONET) return PPM_AF_ECONET; else if (family == AF_ATMSVC) return PPM_AF_ATMSVC; #ifdef AF_RDS else if (family == AF_RDS) return PPM_AF_RDS; #endif else if (family == AF_SNA) return PPM_AF_SNA; else if (family == AF_IRDA) return PPM_AF_IRDA; else if (family == AF_PPPOX) return PPM_AF_PPPOX; else if (family == AF_WANPIPE) return PPM_AF_WANPIPE; else if (family == AF_LLC) return PPM_AF_LLC; #ifdef AF_CAN else if (family == AF_CAN) return PPM_AF_CAN; #endif else if (family == AF_TIPC) return PPM_AF_TIPC; else if (family == AF_BLUETOOTH) return PPM_AF_BLUETOOTH; else if (family == AF_IUCV) return PPM_AF_IUCV; #ifdef AF_RXRPC else if (family == AF_RXRPC) return PPM_AF_RXRPC; #endif #ifdef AF_ISDN else if (family == AF_ISDN) return PPM_AF_ISDN; #endif #ifdef AF_PHONET else if (family == AF_PHONET) return PPM_AF_PHONET; #endif #ifdef AF_IEEE802154 else if (family == AF_IEEE802154) return PPM_AF_IEEE802154; #endif #ifdef AF_CAIF else if (family == AF_CAIF) return PPM_AF_CAIF; #endif #ifdef AF_ALG else if (family == AF_ALG) return PPM_AF_ALG; #endif #ifdef AF_NFC else if (family == AF_NFC) return PPM_AF_NFC; #endif else { ASSERT(false); return PPM_AF_UNSPEC; } } static __always_inline u32 prot_flags_to_scap(int prot) { u32 res = 0; if (prot & PROT_READ) res |= PPM_PROT_READ; if (prot & PROT_WRITE) res |= PPM_PROT_WRITE; if (prot & PROT_EXEC) res |= PPM_PROT_EXEC; if (prot & PROT_SEM) res |= PPM_PROT_SEM; if (prot & PROT_GROWSDOWN) res |= PPM_PROT_GROWSDOWN; if (prot & PROT_GROWSUP) res |= PPM_PROT_GROWSUP; #ifdef PROT_SAO if (prot & PROT_SAO) res |= PPM_PROT_SAO; #endif return res; } static __always_inline u32 mmap_flags_to_scap(int flags) { u32 res = 0; if (flags & MAP_SHARED) res |= PPM_MAP_SHARED; if (flags & MAP_PRIVATE) res |= PPM_MAP_PRIVATE; if (flags & MAP_FIXED) res |= PPM_MAP_FIXED; if (flags & MAP_ANONYMOUS) res |= PPM_MAP_ANONYMOUS; #ifdef MAP_32BIT if (flags & MAP_32BIT) res |= PPM_MAP_32BIT; #endif #ifdef MAP_RENAME if (flags & MAP_RENAME) res |= PPM_MAP_RENAME; #endif if (flags & MAP_NORESERVE) res |= PPM_MAP_NORESERVE; if (flags & MAP_POPULATE) res |= PPM_MAP_POPULATE; if (flags & MAP_NONBLOCK) res |= PPM_MAP_NONBLOCK; if (flags & MAP_GROWSDOWN) res |= PPM_MAP_GROWSDOWN; if (flags & MAP_DENYWRITE) res |= PPM_MAP_DENYWRITE; if (flags & MAP_EXECUTABLE) res |= PPM_MAP_EXECUTABLE; #ifdef MAP_INHERIT if (flags & MAP_INHERIT) res |= PPM_MAP_INHERIT; #endif if (flags & MAP_FILE) res |= PPM_MAP_FILE; if (flags & MAP_LOCKED) res |= PPM_MAP_LOCKED; return res; } static __always_inline u8 fcntl_cmd_to_scap(unsigned long cmd) { switch (cmd) { case F_DUPFD: return PPM_FCNTL_F_DUPFD; case F_GETFD: return PPM_FCNTL_F_GETFD; case F_SETFD: return PPM_FCNTL_F_SETFD; case F_GETFL: return PPM_FCNTL_F_GETFL; case F_SETFL: return PPM_FCNTL_F_SETFL; case F_GETLK: return PPM_FCNTL_F_GETLK; case F_SETLK: return PPM_FCNTL_F_SETLK; case F_SETLKW: return PPM_FCNTL_F_SETLKW; case F_SETOWN: return PPM_FCNTL_F_SETOWN; case F_GETOWN: return PPM_FCNTL_F_GETOWN; case F_SETSIG: return PPM_FCNTL_F_SETSIG; case F_GETSIG: return PPM_FCNTL_F_GETSIG; #ifndef CONFIG_64BIT case F_GETLK64: return PPM_FCNTL_F_GETLK64; case F_SETLK64: return PPM_FCNTL_F_SETLK64; case F_SETLKW64: return PPM_FCNTL_F_SETLKW64; #endif #ifdef F_SETOWN_EX case F_SETOWN_EX: return PPM_FCNTL_F_SETOWN_EX; #endif #ifdef F_GETOWN_EX case F_GETOWN_EX: return PPM_FCNTL_F_GETOWN_EX; #endif case F_SETLEASE: return PPM_FCNTL_F_SETLEASE; case F_GETLEASE: return PPM_FCNTL_F_GETLEASE; case F_CANCELLK: return PPM_FCNTL_F_CANCELLK; #ifdef F_DUPFD_CLOEXEC case F_DUPFD_CLOEXEC: return PPM_FCNTL_F_DUPFD_CLOEXEC; #endif case F_NOTIFY: return PPM_FCNTL_F_NOTIFY; #ifdef F_SETPIPE_SZ case F_SETPIPE_SZ: return PPM_FCNTL_F_SETPIPE_SZ; #endif #ifdef F_GETPIPE_SZ case F_GETPIPE_SZ: return PPM_FCNTL_F_GETPIPE_SZ; #endif #ifdef F_OFD_GETLK case F_OFD_GETLK: return PPM_FCNTL_F_OFD_GETLK; #endif #ifdef F_OFD_SETLK case F_OFD_SETLK: return PPM_FCNTL_F_OFD_SETLK; #endif #ifdef F_OFD_SETLKW case F_OFD_SETLKW: return PPM_FCNTL_F_OFD_SETLKW; #endif default: ASSERT(false); return PPM_FCNTL_UNKNOWN; } } static __always_inline u8 sockopt_level_to_scap(int level) { switch (level) { case SOL_SOCKET: return PPM_SOCKOPT_LEVEL_SOL_SOCKET; case SOL_TCP: return PPM_SOCKOPT_LEVEL_SOL_TCP; default: /* no ASSERT as there are legitimate other levels we don't just support yet */ return PPM_SOCKOPT_LEVEL_UNKNOWN; } } static __always_inline u8 sockopt_optname_to_scap(int level, int optname) { if (level != SOL_SOCKET) { /* no ASSERT as there are legitimate other levels we don't just support yet */ return PPM_SOCKOPT_LEVEL_UNKNOWN; } switch (optname) { #ifdef SO_DEBUG case SO_DEBUG: return PPM_SOCKOPT_SO_DEBUG; #endif #ifdef SO_REUSEADDR case SO_REUSEADDR: return PPM_SOCKOPT_SO_REUSEADDR; #endif #ifdef SO_TYPE case SO_TYPE: return PPM_SOCKOPT_SO_TYPE; #endif #ifdef SO_ERROR case SO_ERROR: return PPM_SOCKOPT_SO_ERROR; #endif #ifdef SO_DONTORUTE case SO_DONTROUTE: return PPM_SOCKOPT_SO_DONTROUTE; #endif #ifdef SO_BROADCAST case SO_BROADCAST: return PPM_SOCKOPT_SO_BROADCAST; #endif #ifdef SO_SNDBUF case SO_SNDBUF: return PPM_SOCKOPT_SO_SNDBUF; #endif #ifdef SO_RCVBUF case SO_RCVBUF: return PPM_SOCKOPT_SO_RCVBUF; #endif #ifdef SO_SNDBUFFORCE case SO_SNDBUFFORCE: return PPM_SOCKOPT_SO_SNDBUFFORCE; #endif #ifdef SO_RCVBUFFORCE case SO_RCVBUFFORCE: return PPM_SOCKOPT_SO_RCVBUFFORCE; #endif #ifdef SO_KEEPALIVE case SO_KEEPALIVE: return PPM_SOCKOPT_SO_KEEPALIVE; #endif #ifdef SO_OOBINLINE case SO_OOBINLINE: return PPM_SOCKOPT_SO_OOBINLINE; #endif #ifdef SO_NO_CHECK case SO_NO_CHECK: return PPM_SOCKOPT_SO_NO_CHECK; #endif #ifdef SO_PRIORITY case SO_PRIORITY: return PPM_SOCKOPT_SO_PRIORITY; #endif #ifdef SO_LINGER case SO_LINGER: return PPM_SOCKOPT_SO_LINGER; #endif #ifdef SO_BSDCOMPAT case SO_BSDCOMPAT: return PPM_SOCKOPT_SO_BSDCOMPAT; #endif #ifdef SO_REUSEPORT case SO_REUSEPORT: return PPM_SOCKOPT_SO_REUSEPORT; #endif #ifdef SO_PASSCRED case SO_PASSCRED: return PPM_SOCKOPT_SO_PASSCRED; #endif #ifdef SO_PEERCRED case SO_PEERCRED: return PPM_SOCKOPT_SO_PEERCRED; #endif #ifdef SO_RCVLOWAT case SO_RCVLOWAT: return PPM_SOCKOPT_SO_RCVLOWAT; #endif #ifdef SO_SNDLOWAT case SO_SNDLOWAT: return PPM_SOCKOPT_SO_SNDLOWAT; #endif #ifdef SO_RCVTIMEO case SO_RCVTIMEO: return PPM_SOCKOPT_SO_RCVTIMEO; #endif #ifdef SO_SNDTIMEO case SO_SNDTIMEO: return PPM_SOCKOPT_SO_SNDTIMEO; #endif #ifdef SO_SECURITY_AUTHENTICATION case SO_SECURITY_AUTHENTICATION: return PPM_SOCKOPT_SO_SECURITY_AUTHENTICATION; #endif #ifdef SO_SECURITY_ENCRYPTION_TRANSPORT case SO_SECURITY_ENCRYPTION_TRANSPORT: return PPM_SOCKOPT_SO_SECURITY_ENCRYPTION_TRANSPORT; #endif #ifdef SO_SECURITY_ENCRYPTION_NETWORK case SO_SECURITY_ENCRYPTION_NETWORK: return PPM_SOCKOPT_SO_SECURITY_ENCRYPTION_NETWORK; #endif #ifdef SO_BINDTODEVICE case SO_BINDTODEVICE: return PPM_SOCKOPT_SO_BINDTODEVICE; #endif #ifdef SO_ATTACH_FILTER case SO_ATTACH_FILTER: return PPM_SOCKOPT_SO_ATTACH_FILTER; #endif #ifdef SO_DETACH_FILTER case SO_DETACH_FILTER: return PPM_SOCKOPT_SO_DETACH_FILTER; #endif #ifdef SO_PEERNAME case SO_PEERNAME: return PPM_SOCKOPT_SO_PEERNAME; #endif #ifdef SO_TIMESTAMP case SO_TIMESTAMP: return PPM_SOCKOPT_SO_TIMESTAMP; #endif #ifdef SO_ACCEPTCONN case SO_ACCEPTCONN: return PPM_SOCKOPT_SO_ACCEPTCONN; #endif #ifdef SO_PEERSEC case SO_PEERSEC: return PPM_SOCKOPT_SO_PEERSEC; #endif #ifdef SO_PASSSEC case SO_PASSSEC: return PPM_SOCKOPT_SO_PASSSEC; #endif #ifdef SO_TIMESTAMPNS case SO_TIMESTAMPNS: return PPM_SOCKOPT_SO_TIMESTAMPNS; #endif #ifdef SO_MARK case SO_MARK: return PPM_SOCKOPT_SO_MARK; #endif #ifdef SO_TIMESTAMPING case SO_TIMESTAMPING: return PPM_SOCKOPT_SO_TIMESTAMPING; #endif #ifdef SO_PROTOCOL case SO_PROTOCOL: return PPM_SOCKOPT_SO_PROTOCOL; #endif #ifdef SO_DOMAIN case SO_DOMAIN: return PPM_SOCKOPT_SO_DOMAIN; #endif #ifdef SO_RXQ_OVFL case SO_RXQ_OVFL: return PPM_SOCKOPT_SO_RXQ_OVFL; #endif #ifdef SO_WIFI_STATUS case SO_WIFI_STATUS: return PPM_SOCKOPT_SO_WIFI_STATUS; #endif #ifdef SO_PEEK_OFF case SO_PEEK_OFF: return PPM_SOCKOPT_SO_PEEK_OFF; #endif #ifdef SO_NOFCS case SO_NOFCS: return PPM_SOCKOPT_SO_NOFCS; #endif #ifdef SO_LOCK_FILTER case SO_LOCK_FILTER: return PPM_SOCKOPT_SO_LOCK_FILTER; #endif #ifdef SO_SELECT_ERR_QUEUE case SO_SELECT_ERR_QUEUE: return PPM_SOCKOPT_SO_SELECT_ERR_QUEUE; #endif #ifdef SO_BUSY_POLL case SO_BUSY_POLL: return PPM_SOCKOPT_SO_BUSY_POLL; #endif #ifdef SO_MAX_PACING_RATE case SO_MAX_PACING_RATE: return PPM_SOCKOPT_SO_MAX_PACING_RATE; #endif #ifdef SO_BPF_EXTENSIONS case SO_BPF_EXTENSIONS: return PPM_SOCKOPT_SO_BPF_EXTENSIONS; #endif #ifdef SO_INCOMING_CPU case SO_INCOMING_CPU: return PPM_SOCKOPT_SO_INCOMING_CPU; #endif #ifdef SO_ATTACH_BPF case SO_ATTACH_BPF: return PPM_SOCKOPT_SO_ATTACH_BPF; #endif #ifdef SO_PEERGROUPS case SO_PEERGROUPS: return PPM_SOCKOPT_SO_PEERGROUPS; #endif #ifdef SO_MEMINFO case SO_MEMINFO: return PPM_SOCKOPT_SO_MEMINFO; #endif #ifdef SO_COOKIE case SO_COOKIE: return PPM_SOCKOPT_SO_COOKIE; #endif default: ASSERT(false); return PPM_SOCKOPT_UNKNOWN; } } /* XXX this is very basic for the moment, we'll need to improve it */ static __always_inline u16 poll_events_to_scap(short revents) { u16 res = 0; if (revents & POLLIN) res |= PPM_POLLIN; if (revents & PPM_POLLPRI) res |= PPM_POLLPRI; if (revents & POLLOUT) res |= PPM_POLLOUT; if (revents & POLLRDHUP) res |= PPM_POLLRDHUP; if (revents & POLLERR) res |= PPM_POLLERR; if (revents & POLLHUP) res |= PPM_POLLHUP; if (revents & POLLNVAL) res |= PPM_POLLNVAL; if (revents & POLLRDNORM) res |= PPM_POLLRDNORM; if (revents & POLLRDBAND) res |= PPM_POLLRDBAND; if (revents & POLLWRNORM) res |= PPM_POLLWRNORM; if (revents & POLLWRBAND) res |= PPM_POLLWRBAND; return res; } static __always_inline u16 futex_op_to_scap(unsigned long op) { u16 res = 0; unsigned long flt_op = op & 127; if (flt_op == FUTEX_WAIT) res = PPM_FU_FUTEX_WAIT; else if (flt_op == FUTEX_WAKE) res = PPM_FU_FUTEX_WAKE; else if (flt_op == FUTEX_FD) res = PPM_FU_FUTEX_FD; else if (flt_op == FUTEX_REQUEUE) res = PPM_FU_FUTEX_REQUEUE; else if (flt_op == FUTEX_CMP_REQUEUE) res = PPM_FU_FUTEX_CMP_REQUEUE; else if (flt_op == FUTEX_WAKE_OP) res = PPM_FU_FUTEX_WAKE_OP; else if (flt_op == FUTEX_LOCK_PI) res = PPM_FU_FUTEX_LOCK_PI; else if (flt_op == FUTEX_UNLOCK_PI) res = PPM_FU_FUTEX_UNLOCK_PI; else if (flt_op == FUTEX_TRYLOCK_PI) res = PPM_FU_FUTEX_TRYLOCK_PI; #ifdef FUTEX_WAIT_BITSET else if (flt_op == FUTEX_WAIT_BITSET) res = PPM_FU_FUTEX_WAIT_BITSET; #endif #ifdef FUTEX_WAKE_BITSET else if (flt_op == FUTEX_WAKE_BITSET) res = PPM_FU_FUTEX_WAKE_BITSET; #endif #ifdef FUTEX_WAIT_REQUEUE_PI else if (flt_op == FUTEX_WAIT_REQUEUE_PI) res = PPM_FU_FUTEX_WAIT_REQUEUE_PI; #endif #ifdef FUTEX_CMP_REQUEUE_PI else if (flt_op == FUTEX_CMP_REQUEUE_PI) res = PPM_FU_FUTEX_CMP_REQUEUE_PI; #endif if (op & FUTEX_PRIVATE_FLAG) res |= PPM_FU_FUTEX_PRIVATE_FLAG; #ifdef FUTEX_CLOCK_REALTIME if (op & FUTEX_CLOCK_REALTIME) res |= PPM_FU_FUTEX_CLOCK_REALTIME; #endif return res; } static __always_inline u32 access_flags_to_scap(unsigned flags) { u32 res = 0; if (flags == 0/*F_OK*/) { res = PPM_F_OK; } else { if (flags & MAY_EXEC) res |= PPM_X_OK; if (flags & MAY_READ) res |= PPM_R_OK; if (flags & MAY_WRITE) res |= PPM_W_OK; } return res; } static __always_inline u8 rlimit_resource_to_scap(unsigned long rresource) { switch (rresource) { case RLIMIT_CPU: return PPM_RLIMIT_CPU; case RLIMIT_FSIZE: return PPM_RLIMIT_FSIZE; case RLIMIT_DATA: return PPM_RLIMIT_DATA; case RLIMIT_STACK: return PPM_RLIMIT_STACK; case RLIMIT_CORE: return PPM_RLIMIT_CORE; case RLIMIT_RSS: return PPM_RLIMIT_RSS; case RLIMIT_NPROC: return PPM_RLIMIT_NPROC; case RLIMIT_NOFILE: return PPM_RLIMIT_NOFILE; case RLIMIT_MEMLOCK: return PPM_RLIMIT_MEMLOCK; case RLIMIT_AS: return PPM_RLIMIT_AS; case RLIMIT_LOCKS: return PPM_RLIMIT_LOCKS; case RLIMIT_SIGPENDING: return PPM_RLIMIT_SIGPENDING; case RLIMIT_MSGQUEUE: return PPM_RLIMIT_MSGQUEUE; case RLIMIT_NICE: return PPM_RLIMIT_NICE; case RLIMIT_RTPRIO: return PPM_RLIMIT_RTPRIO; #ifdef RLIMIT_RTTIME case RLIMIT_RTTIME: return PPM_RLIMIT_RTTIME; #endif default: return PPM_RLIMIT_UNKNOWN; } } static __always_inline u16 shutdown_how_to_scap(unsigned long how) { #ifdef SHUT_RD if (how == SHUT_RD) return PPM_SHUT_RD; else if (how == SHUT_WR) return SHUT_WR; else if (how == SHUT_RDWR) return SHUT_RDWR; ASSERT(false); #endif return (u16)how; } static __always_inline uint64_t lseek_whence_to_scap(unsigned long whence) { uint64_t res = 0; if (whence == SEEK_SET) res = PPM_SEEK_SET; else if (whence == SEEK_CUR) res = PPM_SEEK_CUR; else if (whence == SEEK_END) res = PPM_SEEK_END; return res; } static __always_inline u16 semop_flags_to_scap(short flags) { u16 res = 0; if (flags & IPC_NOWAIT) res |= PPM_IPC_NOWAIT; if (flags & SEM_UNDO) res |= PPM_SEM_UNDO; return res; } static __always_inline u32 pf_flags_to_scap(unsigned long flags) { u32 res = 0; /* Page fault error codes don't seem to be clearly defined in header * files througout the kernel except in some emulation modes (e.g. kvm) * which we can't assume to exist, so I just took the definitions from * the x86 manual. If we end up supporting another arch for page faults, * refactor this. */ if (flags & 0x1) res |= PPM_PF_PROTECTION_VIOLATION; else res |= PPM_PF_PAGE_NOT_PRESENT; if (flags & 0x2) res |= PPM_PF_WRITE_ACCESS; else res |= PPM_PF_READ_ACCESS; if (flags & 0x4) res |= PPM_PF_USER_FAULT; else res |= PPM_PF_SUPERVISOR_FAULT; if (flags & 0x8) res |= PPM_PF_RESERVED_PAGE; if (flags & 0x10) res |= PPM_PF_INSTRUCTION_FETCH; return res; } static __always_inline u32 flock_flags_to_scap(unsigned long flags) { u32 res = 0; if (flags & LOCK_EX) res |= PPM_LOCK_EX; if (flags & LOCK_SH) res |= PPM_LOCK_SH; if (flags & LOCK_UN) res |= PPM_LOCK_UN; if (flags & LOCK_NB) res |= PPM_LOCK_NB; return res; } static __always_inline uint8_t quotactl_type_to_scap(unsigned long cmd) { switch (cmd & SUBCMDMASK) { case USRQUOTA: return PPM_USRQUOTA; case GRPQUOTA: return PPM_GRPQUOTA; } return 0; } static __always_inline uint16_t quotactl_cmd_to_scap(unsigned long cmd) { uint16_t res; switch (cmd >> SUBCMDSHIFT) { case Q_SYNC: res = PPM_Q_SYNC; break; case Q_QUOTAON: res = PPM_Q_QUOTAON; break; case Q_QUOTAOFF: res = PPM_Q_QUOTAOFF; break; case Q_GETFMT: res = PPM_Q_GETFMT; break; case Q_GETINFO: res = PPM_Q_GETINFO; break; case Q_SETINFO: res = PPM_Q_SETINFO; break; case Q_GETQUOTA: res = PPM_Q_GETQUOTA; break; case Q_SETQUOTA: res = PPM_Q_SETQUOTA; break; /* * XFS specific */ case Q_XQUOTAON: res = PPM_Q_XQUOTAON; break; case Q_XQUOTAOFF: res = PPM_Q_XQUOTAOFF; break; case Q_XGETQUOTA: res = PPM_Q_XGETQUOTA; break; case Q_XSETQLIM: res = PPM_Q_XSETQLIM; break; case Q_XGETQSTAT: res = PPM_Q_XGETQSTAT; break; case Q_XQUOTARM: res = PPM_Q_XQUOTARM; break; case Q_XQUOTASYNC: res = PPM_Q_XQUOTASYNC; break; default: res = 0; } return res; } static __always_inline uint8_t quotactl_fmt_to_scap(unsigned long fmt) { switch (fmt) { case QFMT_VFS_OLD: return PPM_QFMT_VFS_OLD; case QFMT_VFS_V0: return PPM_QFMT_VFS_V0; #ifdef QFMT_VFS_V1 case QFMT_VFS_V1: return PPM_QFMT_VFS_V1; #endif default: return PPM_QFMT_NOT_USED; } } static __always_inline u32 semget_flags_to_scap(unsigned flags) { u32 res = 0; if (flags & IPC_CREAT) res |= PPM_IPC_CREAT; if (flags & IPC_EXCL) res |= PPM_IPC_EXCL; return res; } static __always_inline u32 semctl_cmd_to_scap(unsigned cmd) { switch (cmd) { case IPC_STAT: return PPM_IPC_STAT; case IPC_SET: return PPM_IPC_SET; case IPC_RMID: return PPM_IPC_RMID; case IPC_INFO: return PPM_IPC_INFO; case SEM_INFO: return PPM_SEM_INFO; case SEM_STAT: return PPM_SEM_STAT; case GETALL: return PPM_GETALL; case GETNCNT: return PPM_GETNCNT; case GETPID: return PPM_GETPID; case GETVAL: return PPM_GETVAL; case GETZCNT: return PPM_GETZCNT; case SETALL: return PPM_SETALL; case SETVAL: return PPM_SETVAL; } return 0; } static __always_inline u16 ptrace_requests_to_scap(unsigned long req) { switch (req) { #ifdef PTRACE_SINGLEBLOCK case PTRACE_SINGLEBLOCK: return PPM_PTRACE_SINGLEBLOCK; #endif #ifdef PTRACE_SYSEMU_SINGLESTEP case PTRACE_SYSEMU_SINGLESTEP: return PPM_PTRACE_SYSEMU_SINGLESTEP; #endif #ifdef PTRACE_SYSEMU case PTRACE_SYSEMU: return PPM_PTRACE_SYSEMU; #endif #ifdef PTRACE_ARCH_PRCTL case PTRACE_ARCH_PRCTL: return PPM_PTRACE_ARCH_PRCTL; #endif #ifdef PTRACE_SET_THREAD_AREA case PTRACE_SET_THREAD_AREA: return PPM_PTRACE_SET_THREAD_AREA; #endif #ifdef PTRACE_GET_THREAD_AREA case PTRACE_GET_THREAD_AREA: return PPM_PTRACE_GET_THREAD_AREA; #endif #ifdef PTRACE_OLDSETOPTIONS case PTRACE_OLDSETOPTIONS: return PPM_PTRACE_OLDSETOPTIONS; #endif #ifdef PTRACE_SETFPXREGS case PTRACE_SETFPXREGS: return PPM_PTRACE_SETFPXREGS; #endif #ifdef PTRACE_GETFPXREGS case PTRACE_GETFPXREGS: return PPM_PTRACE_GETFPXREGS; #endif #ifdef PTRACE_SETFPREGS case PTRACE_SETFPREGS: return PPM_PTRACE_SETFPREGS; #endif #ifdef PTRACE_GETFPREGS case PTRACE_GETFPREGS: return PPM_PTRACE_GETFPREGS; #endif #ifdef PTRACE_SETREGS case PTRACE_SETREGS: return PPM_PTRACE_SETREGS; #endif #ifdef PTRACE_GETREGS case PTRACE_GETREGS: return PPM_PTRACE_GETREGS; #endif #ifdef PTRACE_SETSIGMASK case PTRACE_SETSIGMASK: return PPM_PTRACE_SETSIGMASK; #endif #ifdef PTRACE_GETSIGMASK case PTRACE_GETSIGMASK: return PPM_PTRACE_GETSIGMASK; #endif #ifdef PTRACE_PEEKSIGINFO case PTRACE_PEEKSIGINFO: return PPM_PTRACE_PEEKSIGINFO; #endif #ifdef PTRACE_LISTEN case PTRACE_LISTEN: return PPM_PTRACE_LISTEN; #endif #ifdef PTRACE_INTERRUPT case PTRACE_INTERRUPT: return PPM_PTRACE_INTERRUPT; #endif #ifdef PTRACE_SEIZE case PTRACE_SEIZE: return PPM_PTRACE_SEIZE; #endif #ifdef PTRACE_SETREGSET case PTRACE_SETREGSET: return PPM_PTRACE_SETREGSET; #endif #ifdef PTRACE_GETREGSET case PTRACE_GETREGSET: return PPM_PTRACE_GETREGSET; #endif case PTRACE_SETSIGINFO: return PPM_PTRACE_SETSIGINFO; case PTRACE_GETSIGINFO: return PPM_PTRACE_GETSIGINFO; case PTRACE_GETEVENTMSG: return PPM_PTRACE_GETEVENTMSG; case PTRACE_SETOPTIONS: return PPM_PTRACE_SETOPTIONS; case PTRACE_SYSCALL: return PPM_PTRACE_SYSCALL; case PTRACE_DETACH: return PPM_PTRACE_DETACH; case PTRACE_ATTACH: return PPM_PTRACE_ATTACH; case PTRACE_SINGLESTEP: return PPM_PTRACE_SINGLESTEP; case PTRACE_KILL: return PPM_PTRACE_KILL; case PTRACE_CONT: return PPM_PTRACE_CONT; case PTRACE_POKEUSR: return PPM_PTRACE_POKEUSR; case PTRACE_POKEDATA: return PPM_PTRACE_POKEDATA; case PTRACE_POKETEXT: return PPM_PTRACE_POKETEXT; case PTRACE_PEEKUSR: return PPM_PTRACE_PEEKUSR; case PTRACE_PEEKDATA: return PPM_PTRACE_PEEKDATA; case PTRACE_PEEKTEXT: return PPM_PTRACE_PEEKTEXT; case PTRACE_TRACEME: return PPM_PTRACE_TRACEME; default: return PPM_PTRACE_UNKNOWN; } } static __always_inline u32 unlinkat_flags_to_scap(unsigned long flags) { u32 res = 0; if (flags & AT_REMOVEDIR) res |= PPM_AT_REMOVEDIR; return res; } static __always_inline u32 linkat_flags_to_scap(unsigned long flags) { u32 res = 0; if (flags & AT_SYMLINK_FOLLOW) res |= PPM_AT_SYMLINK_FOLLOW; #ifdef AT_EMPTY_PATH if (flags & AT_EMPTY_PATH) res |= PPM_AT_EMPTY_PATH; #endif return res; } static __always_inline u32 chmod_mode_to_scap(unsigned long modes) { u32 res = 0; if (modes & S_IRUSR) res |= PPM_S_IRUSR; if (modes & S_IWUSR) res |= PPM_S_IWUSR; if (modes & S_IXUSR) res |= PPM_S_IXUSR; /* * PPM_S_IRWXU == S_IRUSR | S_IWUSR | S_IXUSR */ if (modes & S_IRGRP) res |= PPM_S_IRGRP; if (modes & S_IWGRP) res |= PPM_S_IWGRP; if (modes & S_IXGRP) res |= PPM_S_IXGRP; /* * PPM_S_IRWXG == S_IRGRP | S_IWGRP | S_IXGRP */ if (modes & S_IROTH) res |= PPM_S_IROTH; if (modes & S_IWOTH) res |= PPM_S_IWOTH; if (modes & S_IXOTH) res |= PPM_S_IXOTH; /* * PPM_S_IRWXO == S_IROTH | S_IWOTH | S_IXOTH */ if (modes & S_ISUID) res |= PPM_S_ISUID; if (modes & S_ISGID) res |= PPM_S_ISGID; if (modes & S_ISVTX) res |= PPM_S_ISVTX; return res; } #endif /* PPM_FLAG_HELPERS_H_ */ sysdig-0.26.4/driver/ppm_ringbuffer.h000066400000000000000000000017101352731327100175610ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifndef PPM_RINGBUFFER_H_ #define PPM_RINGBUFFER_H_ #ifdef __KERNEL__ #include #endif static const __u32 RING_BUF_SIZE = 8 * 1024 * 1024; static const __u32 MIN_USERSPACE_READ_SIZE = 128 * 1024; /* * This gets mapped to user level, so we want to keep it as clean as possible */ struct ppm_ring_buffer_info { volatile __u32 head; volatile __u32 tail; volatile __u64 n_evts; /* Total number of events that were received by the driver. */ volatile __u64 n_drops_buffer; /* Number of dropped events (buffer full). */ volatile __u64 n_drops_pf; /* Number of dropped events (page faults). */ volatile __u64 n_preemptions; /* Number of preemptions. */ volatile __u64 n_context_switches; /* Number of received context switch events. */ }; #endif /* PPM_RINGBUFFER_H_ */ sysdig-0.26.4/driver/ppm_syscall.h000066400000000000000000000113341352731327100171050ustar00rootroot00000000000000/* * Access to user system call parameters and results * * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License v.2. * * See asm-generic/syscall.h for descriptions of what we must do here. */ #ifndef _ASM_X86_SYSCALL_H #define _ASM_X86_SYSCALL_H //#include #include #include #include /* For NR_syscalls */ #include /* for TS_COMPAT */ #include #ifndef NS_syscalls #define NR_syscalls (__NR_syscall_max + 1) #endif typedef void (*sys_call_ptr_t)(void); extern const sys_call_ptr_t sys_call_table[]; /* * Only the low 32 bits of orig_ax are meaningful, so we return int. * This importantly ignores the high bits on 64-bit, so comparisons * sign-extend the low 32 bits. */ static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs) { return regs->orig_rax; } static inline void syscall_rollback(struct task_struct *task, struct pt_regs *regs) { regs->rax = regs->orig_rax; } static inline long syscall_get_error(struct task_struct *task, struct pt_regs *regs) { unsigned long error = regs->rax; #ifdef CONFIG_IA32_EMULATION /* * TS_COMPAT is set for 32-bit syscall entries and then * remains set until we return to user mode. */ if (task_thread_info(task)->status & TS_COMPAT) /* * Sign-extend the value so (int)-EFOO becomes (long)-EFOO * and will match correctly in comparisons. */ error = (long) (int) error; #endif return IS_ERR_VALUE(error) ? error : 0; } static inline long syscall_get_return_value(struct task_struct *task, struct pt_regs *regs) { return regs->rax; } static inline void syscall_set_return_value(struct task_struct *task, struct pt_regs *regs, int error, long val) { regs->rax = (long) error ?: val; } #ifdef CONFIG_X86_32 static inline void syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, unsigned int i, unsigned int n, unsigned long *args) { BUG_ON(i + n > 6); memcpy(args, ®s->rbx + i, n * sizeof(args[0])); } static inline void syscall_set_arguments(struct task_struct *task, struct pt_regs *regs, unsigned int i, unsigned int n, const unsigned long *args) { BUG_ON(i + n > 6); memcpy(®s->rbx + i, args, n * sizeof(args[0])); } static inline int syscall_get_arch(void) { return AUDIT_ARCH_I386; } #else /* CONFIG_X86_64 */ static inline void syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, unsigned int i, unsigned int n, unsigned long *args) { # ifdef CONFIG_IA32_EMULATION if (task_thread_info(task)->status & TS_COMPAT) switch (i) { case 0: if (!n--) break; *args++ = regs->rbx; case 1: if (!n--) break; *args++ = regs->rcx; case 2: if (!n--) break; *args++ = regs->rdx; case 3: if (!n--) break; *args++ = regs->rsi; case 4: if (!n--) break; *args++ = regs->rdi; case 5: if (!n--) break; *args++ = regs->rbp; case 6: if (!n--) break; default: BUG(); break; } else # endif switch (i) { case 0: if (!n--) break; *args++ = regs->rdi; case 1: if (!n--) break; *args++ = regs->rsi; case 2: if (!n--) break; *args++ = regs->rdx; case 3: if (!n--) break; *args++ = regs->r10; case 4: if (!n--) break; *args++ = regs->r8; case 5: if (!n--) break; *args++ = regs->r9; case 6: if (!n--) break; default: BUG(); break; } } static inline void syscall_set_arguments(struct task_struct *task, struct pt_regs *regs, unsigned int i, unsigned int n, const unsigned long *args) { # ifdef CONFIG_IA32_EMULATION if (task_thread_info(task)->status & TS_COMPAT) switch (i) { case 0: if (!n--) break; regs->rbx = *args++; case 1: if (!n--) break; regs->rcx = *args++; case 2: if (!n--) break; regs->rdx = *args++; case 3: if (!n--) break; regs->rsi = *args++; case 4: if (!n--) break; regs->rdi = *args++; case 5: if (!n--) break; regs->rbp = *args++; case 6: if (!n--) break; default: BUG(); break; } else # endif switch (i) { case 0: if (!n--) break; regs->rdi = *args++; case 1: if (!n--) break; regs->rsi = *args++; case 2: if (!n--) break; regs->rdx = *args++; case 3: if (!n--) break; regs->r10 = *args++; case 4: if (!n--) break; regs->r8 = *args++; case 5: if (!n--) break; regs->r9 = *args++; case 6: if (!n--) break; default: BUG(); break; } } #endif /* CONFIG_X86_32 */ #endif /* _ASM_X86_SYSCALL_H */ sysdig-0.26.4/driver/ppm_version.h000066400000000000000000000011221352731327100171120ustar00rootroot00000000000000#include /** * for RHEL kernels, export the release code (which is equal to e.g. * RHEL_RELEASE_CODE(8, 1)) under our own name. * For other kernels, just use zeros. * * We need macros that are always defined to use in preprocessor directives * to express the required kernel version in a single expression, without * a multiline #ifdef soup. */ #ifdef RHEL_RELEASE_CODE #define PPM_RHEL_RELEASE_CODE RHEL_RELEASE_CODE #define PPM_RHEL_RELEASE_VERSION(x,y) RHEL_RELEASE_VERSION(x,y) #else #define PPM_RHEL_RELEASE_CODE 0 #define PPM_RHEL_RELEASE_VERSION(x,y) 0 #endif sysdig-0.26.4/driver/syscall_table.c000066400000000000000000002554701352731327100174060ustar00rootroot00000000000000/* Copyright (c) 2013-2018 Draios Inc. dba Sysdig. This file is dual licensed under either the MIT or GPL 2. See MIT.txt or GPL2.txt for full copies of the license. */ #ifdef __KERNEL__ #include #include #include #include #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 20) #include "ppm_syscall.h" #else #include #endif #else /* __KERNEL__ */ #include #define SYSCALL_TABLE_ID0 0 #endif /* __KERNEL__ */ #include "ppm_events_public.h" #ifdef __KERNEL__ #include "ppm.h" #if defined(CONFIG_IA32_EMULATION) && !defined(__NR_ia32_socketcall) #include "ppm_compat_unistd_32.h" #endif #endif /* __KERNEL__ */ /* * SYSCALL TABLE */ const struct syscall_evt_pair g_syscall_table[SYSCALL_TABLE_SIZE] = { [__NR_open - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_OPEN_E, PPME_SYSCALL_OPEN_X}, [__NR_creat - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_CREAT_E, PPME_SYSCALL_CREAT_X}, [__NR_close - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_CLOSE_E, PPME_SYSCALL_CLOSE_X}, [__NR_brk - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_BRK_4_E, PPME_SYSCALL_BRK_4_X}, [__NR_read - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_READ_E, PPME_SYSCALL_READ_X}, [__NR_write - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_WRITE_E, PPME_SYSCALL_WRITE_X}, [__NR_execve - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_EXECVE_19_E, PPME_SYSCALL_EXECVE_19_X}, [__NR_clone - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_CLONE_20_E, PPME_SYSCALL_CLONE_20_X}, [__NR_fork - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_FORK_20_E, PPME_SYSCALL_FORK_20_X}, [__NR_vfork - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_VFORK_20_E, PPME_SYSCALL_VFORK_20_X}, [__NR_pipe - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_PIPE_E, PPME_SYSCALL_PIPE_X}, [__NR_pipe2 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_PIPE_E, PPME_SYSCALL_PIPE_X}, [__NR_eventfd - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_EVENTFD_E, PPME_SYSCALL_EVENTFD_X}, [__NR_eventfd2 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_EVENTFD_E, PPME_SYSCALL_EVENTFD_X}, [__NR_futex - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_FUTEX_E, PPME_SYSCALL_FUTEX_X}, [__NR_stat - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_STAT_E, PPME_SYSCALL_STAT_X}, [__NR_lstat - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_LSTAT_E, PPME_SYSCALL_LSTAT_X}, [__NR_fstat - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_FSTAT_E, PPME_SYSCALL_FSTAT_X}, [__NR_epoll_wait - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_EPOLLWAIT_E, PPME_SYSCALL_EPOLLWAIT_X}, [__NR_poll - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_POLL_E, PPME_SYSCALL_POLL_X}, #ifdef __NR_select [__NR_select - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_SELECT_E, PPME_SYSCALL_SELECT_X}, #endif [__NR_lseek - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_LSEEK_E, PPME_SYSCALL_LSEEK_X}, [__NR_ioctl - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_IOCTL_3_E, PPME_SYSCALL_IOCTL_3_X}, [__NR_getcwd - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_GETCWD_E, PPME_SYSCALL_GETCWD_X}, [__NR_chdir - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_CHDIR_E, PPME_SYSCALL_CHDIR_X}, [__NR_fchdir - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_FCHDIR_E, PPME_SYSCALL_FCHDIR_X}, [__NR_mkdir - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_MKDIR_2_E, PPME_SYSCALL_MKDIR_2_X}, [__NR_rmdir - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_RMDIR_2_E, PPME_SYSCALL_RMDIR_2_X}, [__NR_openat - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_OPENAT_2_E, PPME_SYSCALL_OPENAT_2_X}, [__NR_mkdirat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_MKDIRAT_E, PPME_SYSCALL_MKDIRAT_X}, [__NR_link - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_LINK_2_E, PPME_SYSCALL_LINK_2_X}, [__NR_linkat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_LINKAT_2_E, PPME_SYSCALL_LINKAT_2_X}, [__NR_unlink - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_UNLINK_2_E, PPME_SYSCALL_UNLINK_2_X}, [__NR_unlinkat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_UNLINKAT_2_E, PPME_SYSCALL_UNLINKAT_2_X}, [__NR_pread64 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_PREAD_E, PPME_SYSCALL_PREAD_X}, [__NR_pwrite64 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_PWRITE_E, PPME_SYSCALL_PWRITE_X}, [__NR_readv - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_READV_E, PPME_SYSCALL_READV_X}, [__NR_writev - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_WRITEV_E, PPME_SYSCALL_WRITEV_X}, [__NR_preadv - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_PREADV_E, PPME_SYSCALL_PREADV_X}, [__NR_pwritev - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_PWRITEV_E, PPME_SYSCALL_PWRITEV_X}, [__NR_dup - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_DUP_E, PPME_SYSCALL_DUP_X}, [__NR_dup2 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_DUP_E, PPME_SYSCALL_DUP_X}, [__NR_dup3 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_DUP_E, PPME_SYSCALL_DUP_X}, [__NR_signalfd - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_SIGNALFD_E, PPME_SYSCALL_SIGNALFD_X}, [__NR_signalfd4 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_SIGNALFD_E, PPME_SYSCALL_SIGNALFD_X}, [__NR_kill - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_KILL_E, PPME_SYSCALL_KILL_X}, [__NR_tkill - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_TKILL_E, PPME_SYSCALL_TKILL_X}, [__NR_tgkill - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_TGKILL_E, PPME_SYSCALL_TGKILL_X}, [__NR_nanosleep - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_NANOSLEEP_E, PPME_SYSCALL_NANOSLEEP_X}, [__NR_timerfd_create - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_TIMERFD_CREATE_E, PPME_SYSCALL_TIMERFD_CREATE_X}, [__NR_inotify_init - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_INOTIFY_INIT_E, PPME_SYSCALL_INOTIFY_INIT_X}, [__NR_inotify_init1 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_INOTIFY_INIT_E, PPME_SYSCALL_INOTIFY_INIT_X}, [__NR_fchmodat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_FCHMODAT_E, PPME_SYSCALL_FCHMODAT_X}, [__NR_fchmod - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_FCHMOD_E, PPME_SYSCALL_FCHMOD_X}, #ifdef __NR_getrlimit [__NR_getrlimit - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_GETRLIMIT_E, PPME_SYSCALL_GETRLIMIT_X}, #endif [__NR_setrlimit - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_SETRLIMIT_E, PPME_SYSCALL_SETRLIMIT_X}, #ifdef __NR_prlimit64 [__NR_prlimit64 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_PRLIMIT_E, PPME_SYSCALL_PRLIMIT_X}, #endif #ifdef __NR_ugetrlimit [__NR_ugetrlimit - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_GETRLIMIT_E, PPME_SYSCALL_GETRLIMIT_X}, #endif [__NR_fcntl - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_FCNTL_E, PPME_SYSCALL_FCNTL_X}, #ifdef __NR_fcntl64 [__NR_fcntl64 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_FCNTL_E, PPME_SYSCALL_FCNTL_X}, #endif /* [__NR_old_select - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, */ [__NR_pselect6 - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_epoll_create - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_epoll_ctl - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_uselib - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_sched_setparam - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_sched_getparam - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_syslog - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_chmod - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_CHMOD_E, PPME_SYSCALL_CHMOD_X}, [__NR_lchown - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, #ifdef __NR_utime [__NR_utime - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, #endif [__NR_mount - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_MOUNT_E, PPME_SYSCALL_MOUNT_X}, [__NR_umount2 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_UMOUNT_E, PPME_SYSCALL_UMOUNT_X}, [__NR_ptrace - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_PTRACE_E, PPME_SYSCALL_PTRACE_X}, #ifdef __NR_alarm [__NR_alarm - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, #endif [__NR_pause - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, #ifndef __NR_socketcall [__NR_socket - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SOCKET_SOCKET_E, PPME_SOCKET_SOCKET_X}, [__NR_bind - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SOCKET_BIND_E, PPME_SOCKET_BIND_X}, [__NR_connect - SYSCALL_TABLE_ID0] = {UF_USED | UF_SIMPLEDRIVER_KEEP, PPME_SOCKET_CONNECT_E, PPME_SOCKET_CONNECT_X}, [__NR_listen - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_LISTEN_E, PPME_SOCKET_LISTEN_X}, [__NR_accept - SYSCALL_TABLE_ID0] = {UF_USED | UF_SIMPLEDRIVER_KEEP, PPME_SOCKET_ACCEPT_5_E, PPME_SOCKET_ACCEPT_5_X}, [__NR_getsockname - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SOCKET_GETSOCKNAME_E, PPME_SOCKET_GETSOCKNAME_X}, [__NR_getpeername - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SOCKET_GETPEERNAME_E, PPME_SOCKET_GETPEERNAME_X}, [__NR_socketpair - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SOCKET_SOCKETPAIR_E, PPME_SOCKET_SOCKETPAIR_X}, [__NR_sendto - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_SENDTO_E, PPME_SOCKET_SENDTO_X}, [__NR_recvfrom - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_RECVFROM_E, PPME_SOCKET_RECVFROM_X}, [__NR_shutdown - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_SHUTDOWN_E, PPME_SOCKET_SHUTDOWN_X}, [__NR_setsockopt - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SOCKET_SETSOCKOPT_E, PPME_SOCKET_SETSOCKOPT_X}, [__NR_getsockopt - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_GETSOCKOPT_E, PPME_SOCKET_GETSOCKOPT_X}, [__NR_sendmsg - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_SENDMSG_E, PPME_SOCKET_SENDMSG_X}, [__NR_accept4 - SYSCALL_TABLE_ID0] = {UF_USED | UF_SIMPLEDRIVER_KEEP, PPME_SOCKET_ACCEPT4_5_E, PPME_SOCKET_ACCEPT4_5_X}, #endif #ifdef __NR_sendmmsg [__NR_sendmmsg - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_SENDMMSG_E, PPME_SOCKET_SENDMMSG_X}, #endif #ifdef __NR_recvmsg [__NR_recvmsg - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_RECVMSG_E, PPME_SOCKET_RECVMSG_X}, #endif #ifdef __NR_recvmmsg [__NR_recvmmsg - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_RECVMMSG_E, PPME_SOCKET_RECVMMSG_X}, #endif #ifdef __NR_stat64 [__NR_stat64 - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_STAT64_E, PPME_SYSCALL_STAT64_X}, #endif #ifdef __NR_fstat64 [__NR_fstat64 - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_FSTAT64_E, PPME_SYSCALL_FSTAT64_X}, #endif #ifdef __NR__llseek [__NR__llseek - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_LLSEEK_E, PPME_SYSCALL_LLSEEK_X}, #endif #ifdef __NR_mmap [__NR_mmap - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_MMAP_E, PPME_SYSCALL_MMAP_X}, #endif #ifdef __NR_mmap2 [__NR_mmap2 - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_MMAP2_E, PPME_SYSCALL_MMAP2_X}, #endif [__NR_munmap - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_MUNMAP_E, PPME_SYSCALL_MUNMAP_X}, [__NR_splice - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SPLICE_E, PPME_SYSCALL_SPLICE_X}, #ifdef __NR_process_vm_readv [__NR_process_vm_readv - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, #endif #ifdef __NR_process_vm_writev [__NR_process_vm_writev - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, #endif [__NR_rename - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_RENAME_E, PPME_SYSCALL_RENAME_X}, [__NR_renameat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_RENAMEAT_E, PPME_SYSCALL_RENAMEAT_X}, [__NR_symlink - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SYMLINK_E, PPME_SYSCALL_SYMLINK_X}, [__NR_symlinkat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SYMLINKAT_E, PPME_SYSCALL_SYMLINKAT_X}, [__NR_sendfile - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SENDFILE_E, PPME_SYSCALL_SENDFILE_X}, #ifdef __NR_sendfile64 [__NR_sendfile64 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SENDFILE_E, PPME_SYSCALL_SENDFILE_X}, #endif #ifdef __NR_quotactl [__NR_quotactl - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_QUOTACTL_E, PPME_SYSCALL_QUOTACTL_X}, #endif #ifdef __NR_setresuid [__NR_setresuid - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SETRESUID_E, PPME_SYSCALL_SETRESUID_X }, #endif #ifdef __NR_setresuid32 [__NR_setresuid32 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SETRESUID_E, PPME_SYSCALL_SETRESUID_X }, #endif #ifdef __NR_setresgid [__NR_setresgid - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SETRESGID_E, PPME_SYSCALL_SETRESGID_X }, #endif #ifdef __NR_setresgid32 [__NR_setresgid32 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SETRESGID_E, PPME_SYSCALL_SETRESGID_X }, #endif #ifdef __NR_setuid [__NR_setuid - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SETUID_E, PPME_SYSCALL_SETUID_X }, #endif #ifdef __NR_setuid32 [__NR_setuid32 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SETUID_E, PPME_SYSCALL_SETUID_X }, #endif #ifdef __NR_setgid [__NR_setgid - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SETGID_E, PPME_SYSCALL_SETGID_X }, #endif #ifdef __NR_setgid32 [__NR_setgid32 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SETGID_E, PPME_SYSCALL_SETGID_X }, #endif #ifdef __NR_getuid [__NR_getuid - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_GETUID_E, PPME_SYSCALL_GETUID_X }, #endif #ifdef __NR_getuid32 [__NR_getuid32 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_GETUID_E, PPME_SYSCALL_GETUID_X }, #endif #ifdef __NR_geteuid [__NR_geteuid - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_GETEUID_E, PPME_SYSCALL_GETEUID_X }, #endif #ifdef __NR_geteuid32 [__NR_geteuid32 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_GETEUID_E, PPME_SYSCALL_GETEUID_X }, #endif #ifdef __NR_getgid [__NR_getgid - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_GETGID_E, PPME_SYSCALL_GETGID_X }, #endif #ifdef __NR_getgid32 [__NR_getgid32 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_GETGID_E, PPME_SYSCALL_GETGID_X }, #endif #ifdef __NR_getegid [__NR_getegid - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_GETEGID_E, PPME_SYSCALL_GETEGID_X }, #endif #ifdef __NR_getegid32 [__NR_getegid32 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_GETEGID_E, PPME_SYSCALL_GETEGID_X }, #endif #ifdef __NR_getresuid [__NR_getresuid - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_GETRESUID_E, PPME_SYSCALL_GETRESUID_X }, #endif #ifdef __NR_getresuid32 [__NR_getresuid32 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_GETRESUID_E, PPME_SYSCALL_GETRESUID_X }, #endif #ifdef __NR_getresgid [__NR_getresgid - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_GETRESGID_E, PPME_SYSCALL_GETRESGID_X }, #endif #ifdef __NR_getresgid32 [__NR_getresgid32 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_GETRESGID_E, PPME_SYSCALL_GETRESGID_X }, #endif [__NR_getdents - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_GETDENTS_E, PPME_SYSCALL_GETDENTS_X}, [__NR_getdents64 - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_GETDENTS64_E, PPME_SYSCALL_GETDENTS64_X}, #ifdef __NR_setns [__NR_setns - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SETNS_E, PPME_SYSCALL_SETNS_X}, #endif #ifdef __NR_unshare [__NR_unshare - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_UNSHARE_E, PPME_SYSCALL_UNSHARE_X}, #endif [__NR_flock - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_FLOCK_E, PPME_SYSCALL_FLOCK_X}, #ifdef __NR_semop [__NR_semop - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_SEMOP_E, PPME_SYSCALL_SEMOP_X}, #endif #ifdef __NR_semget [__NR_semget - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_SEMGET_E, PPME_SYSCALL_SEMGET_X}, #endif #ifdef __NR_semctl [__NR_semctl - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_SEMCTL_E, PPME_SYSCALL_SEMCTL_X}, #endif [__NR_ppoll - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_PPOLL_E, PPME_SYSCALL_PPOLL_X}, #ifdef __NR_access [__NR_access - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_ACCESS_E, PPME_SYSCALL_ACCESS_X}, #endif #ifdef __NR_chroot [__NR_chroot - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_CHROOT_E, PPME_SYSCALL_CHROOT_X}, #endif [__NR_setsid - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_SETSID_E, PPME_SYSCALL_SETSID_X}, [__NR_setpgid - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_SETPGID_E, PPME_SYSCALL_SETPGID_X}, #ifdef __NR_bpf [__NR_bpf - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_BPF_E, PPME_SYSCALL_BPF_X}, #endif #ifdef __NR_seccomp [__NR_seccomp - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SECCOMP_E, PPME_SYSCALL_SECCOMP_X}, #endif }; /* * SYSCALL ROUTING TABLE */ const enum ppm_syscall_code g_syscall_code_routing_table[SYSCALL_TABLE_SIZE] = { [__NR_restart_syscall - SYSCALL_TABLE_ID0] = PPM_SC_RESTART_SYSCALL, [__NR_exit - SYSCALL_TABLE_ID0] = PPM_SC_EXIT, [__NR_read - SYSCALL_TABLE_ID0] = PPM_SC_READ, [__NR_write - SYSCALL_TABLE_ID0] = PPM_SC_WRITE, [__NR_open - SYSCALL_TABLE_ID0] = PPM_SC_OPEN, [__NR_close - SYSCALL_TABLE_ID0] = PPM_SC_CLOSE, [__NR_creat - SYSCALL_TABLE_ID0] = PPM_SC_CREAT, [__NR_link - SYSCALL_TABLE_ID0] = PPM_SC_LINK, [__NR_unlink - SYSCALL_TABLE_ID0] = PPM_SC_UNLINK, [__NR_chdir - SYSCALL_TABLE_ID0] = PPM_SC_CHDIR, #ifdef __NR_time [__NR_time - SYSCALL_TABLE_ID0] = PPM_SC_TIME, #endif [__NR_mknod - SYSCALL_TABLE_ID0] = PPM_SC_MKNOD, [__NR_chmod - SYSCALL_TABLE_ID0] = PPM_SC_CHMOD, /* [__NR_lchown16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_LCHOWN16, */ [__NR_stat - SYSCALL_TABLE_ID0] = PPM_SC_STAT, [__NR_lseek - SYSCALL_TABLE_ID0] = PPM_SC_LSEEK, [__NR_getpid - SYSCALL_TABLE_ID0] = PPM_SC_GETPID, [__NR_mount - SYSCALL_TABLE_ID0] = PPM_SC_MOUNT, /* [__NR_oldumount - SYSCALL_TABLE_ID0] = PPM_SC_NR_OLDUMOUNT, */ /* [__NR_setuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETUID16, */ /* [__NR_getuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETUID16, */ [__NR_ptrace - SYSCALL_TABLE_ID0] = PPM_SC_PTRACE, #ifdef __NR_alarm [__NR_alarm - SYSCALL_TABLE_ID0] = PPM_SC_ALARM, #endif [__NR_fstat - SYSCALL_TABLE_ID0] = PPM_SC_FSTAT, [__NR_pause - SYSCALL_TABLE_ID0] = PPM_SC_PAUSE, #ifdef __NR_utime [__NR_utime - SYSCALL_TABLE_ID0] = PPM_SC_UTIME, #endif [__NR_sync - SYSCALL_TABLE_ID0] = PPM_SC_SYNC, [__NR_kill - SYSCALL_TABLE_ID0] = PPM_SC_KILL, [__NR_rename - SYSCALL_TABLE_ID0] = PPM_SC_RENAME, [__NR_mkdir - SYSCALL_TABLE_ID0] = PPM_SC_MKDIR, [__NR_rmdir - SYSCALL_TABLE_ID0] = PPM_SC_RMDIR, [__NR_dup - SYSCALL_TABLE_ID0] = PPM_SC_DUP, [__NR_pipe - SYSCALL_TABLE_ID0] = PPM_SC_PIPE, [__NR_times - SYSCALL_TABLE_ID0] = PPM_SC_TIMES, [__NR_brk - SYSCALL_TABLE_ID0] = PPM_SC_BRK, /* [__NR_setgid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETGID16, */ /* [__NR_getgid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETGID16, */ /* [__NR_geteuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETEUID16, */ /* [__NR_getegid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETEGID16, */ [__NR_acct - SYSCALL_TABLE_ID0] = PPM_SC_ACCT, [__NR_ioctl - SYSCALL_TABLE_ID0] = PPM_SC_IOCTL, [__NR_fcntl - SYSCALL_TABLE_ID0] = PPM_SC_FCNTL, [__NR_setpgid - SYSCALL_TABLE_ID0] = PPM_SC_SETPGID, [__NR_umask - SYSCALL_TABLE_ID0] = PPM_SC_UMASK, [__NR_chroot - SYSCALL_TABLE_ID0] = PPM_SC_CHROOT, [__NR_ustat - SYSCALL_TABLE_ID0] = PPM_SC_USTAT, [__NR_dup2 - SYSCALL_TABLE_ID0] = PPM_SC_DUP2, [__NR_getppid - SYSCALL_TABLE_ID0] = PPM_SC_GETPPID, [__NR_getpgrp - SYSCALL_TABLE_ID0] = PPM_SC_GETPGRP, [__NR_setsid - SYSCALL_TABLE_ID0] = PPM_SC_SETSID, [__NR_sethostname - SYSCALL_TABLE_ID0] = PPM_SC_SETHOSTNAME, [__NR_setrlimit - SYSCALL_TABLE_ID0] = PPM_SC_SETRLIMIT, /* [__NR_old_getrlimit - SYSCALL_TABLE_ID0] = PPM_SC_NR_OLD_GETRLIMIT, */ [__NR_getrusage - SYSCALL_TABLE_ID0] = PPM_SC_GETRUSAGE, [__NR_gettimeofday - SYSCALL_TABLE_ID0] = PPM_SC_GETTIMEOFDAY, [__NR_settimeofday - SYSCALL_TABLE_ID0] = PPM_SC_SETTIMEOFDAY, /* [__NR_getgroups16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETGROUPS16, */ /* [__NR_setgroups16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETGROUPS16, */ /* [__NR_old_select - SYSCALL_TABLE_ID0] = PPM_SC_NR_OLD_SELECT, */ [__NR_symlink - SYSCALL_TABLE_ID0] = PPM_SC_SYMLINK, [__NR_lstat - SYSCALL_TABLE_ID0] = PPM_SC_LSTAT, [__NR_readlink - SYSCALL_TABLE_ID0] = PPM_SC_READLINK, [__NR_uselib - SYSCALL_TABLE_ID0] = PPM_SC_USELIB, [__NR_swapon - SYSCALL_TABLE_ID0] = PPM_SC_SWAPON, [__NR_reboot - SYSCALL_TABLE_ID0] = PPM_SC_REBOOT, /* [__NR_old_readdir - SYSCALL_TABLE_ID0] = PPM_SC_NR_OLD_READDIR, */ /* [__NR_old_mmap - SYSCALL_TABLE_ID0] = PPM_SC_NR_OLD_MMAP, */ #ifdef __NR_mmap [__NR_mmap - SYSCALL_TABLE_ID0] = PPM_SC_MMAP, #endif [__NR_munmap - SYSCALL_TABLE_ID0] = PPM_SC_MUNMAP, [__NR_truncate - SYSCALL_TABLE_ID0] = PPM_SC_TRUNCATE, [__NR_ftruncate - SYSCALL_TABLE_ID0] = PPM_SC_FTRUNCATE, [__NR_fchmod - SYSCALL_TABLE_ID0] = PPM_SC_FCHMOD, /* [__NR_fchown16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_FCHOWN16, */ [__NR_getpriority - SYSCALL_TABLE_ID0] = PPM_SC_GETPRIORITY, [__NR_setpriority - SYSCALL_TABLE_ID0] = PPM_SC_SETPRIORITY, [__NR_statfs - SYSCALL_TABLE_ID0] = PPM_SC_STATFS, [__NR_fstatfs - SYSCALL_TABLE_ID0] = PPM_SC_FSTATFS, [__NR_syslog - SYSCALL_TABLE_ID0] = PPM_SC_SYSLOG, [__NR_setitimer - SYSCALL_TABLE_ID0] = PPM_SC_SETITIMER, [__NR_getitimer - SYSCALL_TABLE_ID0] = PPM_SC_GETITIMER, /* [__NR_newstat - SYSCALL_TABLE_ID0] = PPM_SC_NR_NEWSTAT, */ /* [__NR_newlstat - SYSCALL_TABLE_ID0] = PPM_SC_NR_NEWLSTAT, */ /* [__NR_newfstat - SYSCALL_TABLE_ID0] = PPM_SC_NR_NEWFSTAT, */ [__NR_uname - SYSCALL_TABLE_ID0] = PPM_SC_UNAME, [__NR_vhangup - SYSCALL_TABLE_ID0] = PPM_SC_VHANGUP, [__NR_wait4 - SYSCALL_TABLE_ID0] = PPM_SC_WAIT4, [__NR_swapoff - SYSCALL_TABLE_ID0] = PPM_SC_SWAPOFF, [__NR_sysinfo - SYSCALL_TABLE_ID0] = PPM_SC_SYSINFO, [__NR_fsync - SYSCALL_TABLE_ID0] = PPM_SC_FSYNC, [__NR_setdomainname - SYSCALL_TABLE_ID0] = PPM_SC_SETDOMAINNAME, /* [__NR_newuname - SYSCALL_TABLE_ID0] = PPM_SC_NR_NEWUNAME, */ [__NR_adjtimex - SYSCALL_TABLE_ID0] = PPM_SC_ADJTIMEX, [__NR_mprotect - SYSCALL_TABLE_ID0] = PPM_SC_MPROTECT, [__NR_init_module - SYSCALL_TABLE_ID0] = PPM_SC_INIT_MODULE, [__NR_delete_module - SYSCALL_TABLE_ID0] = PPM_SC_DELETE_MODULE, [__NR_getpgid - SYSCALL_TABLE_ID0] = PPM_SC_GETPGID, [__NR_fchdir - SYSCALL_TABLE_ID0] = PPM_SC_FCHDIR, [__NR_sysfs - SYSCALL_TABLE_ID0] = PPM_SC_SYSFS, [__NR_personality - SYSCALL_TABLE_ID0] = PPM_SC_PERSONALITY, /* [__NR_setfsuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETFSUID16, */ /* [__NR_setfsgid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETFSGID16, */ /* [__NR_llseek - SYSCALL_TABLE_ID0] = PPM_SC_NR_LLSEEK, */ [__NR_getdents - SYSCALL_TABLE_ID0] = PPM_SC_GETDENTS, #ifdef __NR_select [__NR_select - SYSCALL_TABLE_ID0] = PPM_SC_SELECT, #endif [__NR_flock - SYSCALL_TABLE_ID0] = PPM_SC_FLOCK, [__NR_msync - SYSCALL_TABLE_ID0] = PPM_SC_MSYNC, [__NR_readv - SYSCALL_TABLE_ID0] = PPM_SC_READV, [__NR_writev - SYSCALL_TABLE_ID0] = PPM_SC_WRITEV, [__NR_getsid - SYSCALL_TABLE_ID0] = PPM_SC_GETSID, [__NR_fdatasync - SYSCALL_TABLE_ID0] = PPM_SC_FDATASYNC, /* [__NR_sysctl - SYSCALL_TABLE_ID0] = PPM_SC_NR_SYSCTL, */ [__NR_mlock - SYSCALL_TABLE_ID0] = PPM_SC_MLOCK, [__NR_munlock - SYSCALL_TABLE_ID0] = PPM_SC_MUNLOCK, [__NR_mlockall - SYSCALL_TABLE_ID0] = PPM_SC_MLOCKALL, [__NR_munlockall - SYSCALL_TABLE_ID0] = PPM_SC_MUNLOCKALL, [__NR_sched_setparam - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_SETPARAM, [__NR_sched_getparam - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_GETPARAM, [__NR_sched_setscheduler - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_SETSCHEDULER, [__NR_sched_getscheduler - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_GETSCHEDULER, [__NR_sched_yield - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_YIELD, [__NR_sched_get_priority_max - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_GET_PRIORITY_MAX, [__NR_sched_get_priority_min - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_GET_PRIORITY_MIN, [__NR_sched_rr_get_interval - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_RR_GET_INTERVAL, [__NR_nanosleep - SYSCALL_TABLE_ID0] = PPM_SC_NANOSLEEP, [__NR_mremap - SYSCALL_TABLE_ID0] = PPM_SC_MREMAP, /* [__NR_setresuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETRESUID16, */ /* [__NR_getresuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETRESUID16, */ [__NR_poll - SYSCALL_TABLE_ID0] = PPM_SC_POLL, /* [__NR_setresgid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETRESGID16, */ /* [__NR_getresgid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETRESGID16, */ [__NR_prctl - SYSCALL_TABLE_ID0] = PPM_SC_PRCTL, #ifdef __NR_arch_prctl [__NR_arch_prctl - SYSCALL_TABLE_ID0] = PPM_SC_ARCH_PRCTL, #endif [__NR_rt_sigaction - SYSCALL_TABLE_ID0] = PPM_SC_RT_SIGACTION, [__NR_rt_sigprocmask - SYSCALL_TABLE_ID0] = PPM_SC_RT_SIGPROCMASK, [__NR_rt_sigpending - SYSCALL_TABLE_ID0] = PPM_SC_RT_SIGPENDING, [__NR_rt_sigtimedwait - SYSCALL_TABLE_ID0] = PPM_SC_RT_SIGTIMEDWAIT, [__NR_rt_sigqueueinfo - SYSCALL_TABLE_ID0] = PPM_SC_RT_SIGQUEUEINFO, [__NR_rt_sigsuspend - SYSCALL_TABLE_ID0] = PPM_SC_RT_SIGSUSPEND, /* [__NR_chown16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_CHOWN16, */ [__NR_getcwd - SYSCALL_TABLE_ID0] = PPM_SC_GETCWD, [__NR_capget - SYSCALL_TABLE_ID0] = PPM_SC_CAPGET, [__NR_capset - SYSCALL_TABLE_ID0] = PPM_SC_CAPSET, [__NR_sendfile - SYSCALL_TABLE_ID0] = PPM_SC_SENDFILE, #ifdef __NR_getrlimit [__NR_getrlimit - SYSCALL_TABLE_ID0] = PPM_SC_GETRLIMIT, #endif /* [__NR_mmap_pgoff - SYSCALL_TABLE_ID0] = PPM_SC_NR_MMAP_PGOFF, */ [__NR_lchown - SYSCALL_TABLE_ID0] = PPM_SC_LCHOWN, [__NR_setreuid - SYSCALL_TABLE_ID0] = PPM_SC_SETREUID, [__NR_setregid - SYSCALL_TABLE_ID0] = PPM_SC_SETREGID, [__NR_getgroups - SYSCALL_TABLE_ID0] = PPM_SC_GETGROUPS, [__NR_setgroups - SYSCALL_TABLE_ID0] = PPM_SC_SETGROUPS, [__NR_fchown - SYSCALL_TABLE_ID0] = PPM_SC_FCHOWN, [__NR_chown - SYSCALL_TABLE_ID0] = PPM_SC_CHOWN, [__NR_setfsuid - SYSCALL_TABLE_ID0] = PPM_SC_SETFSUID, [__NR_setfsgid - SYSCALL_TABLE_ID0] = PPM_SC_SETFSGID, [__NR_pivot_root - SYSCALL_TABLE_ID0] = PPM_SC_PIVOT_ROOT, [__NR_mincore - SYSCALL_TABLE_ID0] = PPM_SC_MINCORE, [__NR_madvise - SYSCALL_TABLE_ID0] = PPM_SC_MADVISE, [__NR_gettid - SYSCALL_TABLE_ID0] = PPM_SC_GETTID, [__NR_setxattr - SYSCALL_TABLE_ID0] = PPM_SC_SETXATTR, [__NR_lsetxattr - SYSCALL_TABLE_ID0] = PPM_SC_LSETXATTR, [__NR_fsetxattr - SYSCALL_TABLE_ID0] = PPM_SC_FSETXATTR, [__NR_getxattr - SYSCALL_TABLE_ID0] = PPM_SC_GETXATTR, [__NR_lgetxattr - SYSCALL_TABLE_ID0] = PPM_SC_LGETXATTR, [__NR_fgetxattr - SYSCALL_TABLE_ID0] = PPM_SC_FGETXATTR, [__NR_listxattr - SYSCALL_TABLE_ID0] = PPM_SC_LISTXATTR, [__NR_llistxattr - SYSCALL_TABLE_ID0] = PPM_SC_LLISTXATTR, [__NR_flistxattr - SYSCALL_TABLE_ID0] = PPM_SC_FLISTXATTR, [__NR_removexattr - SYSCALL_TABLE_ID0] = PPM_SC_REMOVEXATTR, [__NR_lremovexattr - SYSCALL_TABLE_ID0] = PPM_SC_LREMOVEXATTR, [__NR_fremovexattr - SYSCALL_TABLE_ID0] = PPM_SC_FREMOVEXATTR, [__NR_tkill - SYSCALL_TABLE_ID0] = PPM_SC_TKILL, [__NR_futex - SYSCALL_TABLE_ID0] = PPM_SC_FUTEX, [__NR_sched_setaffinity - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_SETAFFINITY, [__NR_sched_getaffinity - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_GETAFFINITY, #ifdef __NR_set_thread_area [__NR_set_thread_area - SYSCALL_TABLE_ID0] = PPM_SC_SET_THREAD_AREA, #endif #ifdef __NR_get_thread_area [__NR_get_thread_area - SYSCALL_TABLE_ID0] = PPM_SC_GET_THREAD_AREA, #endif [__NR_io_setup - SYSCALL_TABLE_ID0] = PPM_SC_IO_SETUP, [__NR_io_destroy - SYSCALL_TABLE_ID0] = PPM_SC_IO_DESTROY, [__NR_io_getevents - SYSCALL_TABLE_ID0] = PPM_SC_IO_GETEVENTS, [__NR_io_submit - SYSCALL_TABLE_ID0] = PPM_SC_IO_SUBMIT, [__NR_io_cancel - SYSCALL_TABLE_ID0] = PPM_SC_IO_CANCEL, [__NR_exit_group - SYSCALL_TABLE_ID0] = PPM_SC_EXIT_GROUP, [__NR_epoll_create - SYSCALL_TABLE_ID0] = PPM_SC_EPOLL_CREATE, [__NR_epoll_ctl - SYSCALL_TABLE_ID0] = PPM_SC_EPOLL_CTL, [__NR_epoll_wait - SYSCALL_TABLE_ID0] = PPM_SC_EPOLL_WAIT, [__NR_remap_file_pages - SYSCALL_TABLE_ID0] = PPM_SC_REMAP_FILE_PAGES, [__NR_set_tid_address - SYSCALL_TABLE_ID0] = PPM_SC_SET_TID_ADDRESS, [__NR_timer_create - SYSCALL_TABLE_ID0] = PPM_SC_TIMER_CREATE, [__NR_timer_settime - SYSCALL_TABLE_ID0] = PPM_SC_TIMER_SETTIME, [__NR_timer_gettime - SYSCALL_TABLE_ID0] = PPM_SC_TIMER_GETTIME, [__NR_timer_getoverrun - SYSCALL_TABLE_ID0] = PPM_SC_TIMER_GETOVERRUN, [__NR_timer_delete - SYSCALL_TABLE_ID0] = PPM_SC_TIMER_DELETE, [__NR_clock_settime - SYSCALL_TABLE_ID0] = PPM_SC_CLOCK_SETTIME, [__NR_clock_gettime - SYSCALL_TABLE_ID0] = PPM_SC_CLOCK_GETTIME, [__NR_clock_getres - SYSCALL_TABLE_ID0] = PPM_SC_CLOCK_GETRES, [__NR_clock_nanosleep - SYSCALL_TABLE_ID0] = PPM_SC_CLOCK_NANOSLEEP, [__NR_tgkill - SYSCALL_TABLE_ID0] = PPM_SC_TGKILL, [__NR_utimes - SYSCALL_TABLE_ID0] = PPM_SC_UTIMES, [__NR_mq_open - SYSCALL_TABLE_ID0] = PPM_SC_MQ_OPEN, [__NR_mq_unlink - SYSCALL_TABLE_ID0] = PPM_SC_MQ_UNLINK, [__NR_mq_timedsend - SYSCALL_TABLE_ID0] = PPM_SC_MQ_TIMEDSEND, [__NR_mq_timedreceive - SYSCALL_TABLE_ID0] = PPM_SC_MQ_TIMEDRECEIVE, [__NR_mq_notify - SYSCALL_TABLE_ID0] = PPM_SC_MQ_NOTIFY, [__NR_mq_getsetattr - SYSCALL_TABLE_ID0] = PPM_SC_MQ_GETSETATTR, [__NR_kexec_load - SYSCALL_TABLE_ID0] = PPM_SC_KEXEC_LOAD, [__NR_waitid - SYSCALL_TABLE_ID0] = PPM_SC_WAITID, [__NR_add_key - SYSCALL_TABLE_ID0] = PPM_SC_ADD_KEY, [__NR_request_key - SYSCALL_TABLE_ID0] = PPM_SC_REQUEST_KEY, [__NR_keyctl - SYSCALL_TABLE_ID0] = PPM_SC_KEYCTL, [__NR_ioprio_set - SYSCALL_TABLE_ID0] = PPM_SC_IOPRIO_SET, [__NR_ioprio_get - SYSCALL_TABLE_ID0] = PPM_SC_IOPRIO_GET, [__NR_inotify_init - SYSCALL_TABLE_ID0] = PPM_SC_INOTIFY_INIT, [__NR_inotify_add_watch - SYSCALL_TABLE_ID0] = PPM_SC_INOTIFY_ADD_WATCH, [__NR_inotify_rm_watch - SYSCALL_TABLE_ID0] = PPM_SC_INOTIFY_RM_WATCH, [__NR_openat - SYSCALL_TABLE_ID0] = PPM_SC_OPENAT, [__NR_mkdirat - SYSCALL_TABLE_ID0] = PPM_SC_MKDIRAT, [__NR_mknodat - SYSCALL_TABLE_ID0] = PPM_SC_MKNODAT, [__NR_fchownat - SYSCALL_TABLE_ID0] = PPM_SC_FCHOWNAT, [__NR_futimesat - SYSCALL_TABLE_ID0] = PPM_SC_FUTIMESAT, [__NR_unlinkat - SYSCALL_TABLE_ID0] = PPM_SC_UNLINKAT, [__NR_renameat - SYSCALL_TABLE_ID0] = PPM_SC_RENAMEAT, [__NR_linkat - SYSCALL_TABLE_ID0] = PPM_SC_LINKAT, [__NR_symlinkat - SYSCALL_TABLE_ID0] = PPM_SC_SYMLINKAT, [__NR_readlinkat - SYSCALL_TABLE_ID0] = PPM_SC_READLINKAT, [__NR_fchmodat - SYSCALL_TABLE_ID0] = PPM_SC_FCHMODAT, [__NR_faccessat - SYSCALL_TABLE_ID0] = PPM_SC_FACCESSAT, [__NR_pselect6 - SYSCALL_TABLE_ID0] = PPM_SC_PSELECT6, [__NR_ppoll - SYSCALL_TABLE_ID0] = PPM_SC_PPOLL, [__NR_unshare - SYSCALL_TABLE_ID0] = PPM_SC_UNSHARE, [__NR_set_robust_list - SYSCALL_TABLE_ID0] = PPM_SC_SET_ROBUST_LIST, [__NR_get_robust_list - SYSCALL_TABLE_ID0] = PPM_SC_GET_ROBUST_LIST, [__NR_splice - SYSCALL_TABLE_ID0] = PPM_SC_SPLICE, [__NR_tee - SYSCALL_TABLE_ID0] = PPM_SC_TEE, [__NR_vmsplice - SYSCALL_TABLE_ID0] = PPM_SC_VMSPLICE, #ifdef __NR_getcpu [__NR_getcpu - SYSCALL_TABLE_ID0] = PPM_SC_GETCPU, #endif [__NR_epoll_pwait - SYSCALL_TABLE_ID0] = PPM_SC_EPOLL_PWAIT, [__NR_utimensat - SYSCALL_TABLE_ID0] = PPM_SC_UTIMENSAT, [__NR_signalfd - SYSCALL_TABLE_ID0] = PPM_SC_SIGNALFD, [__NR_timerfd_create - SYSCALL_TABLE_ID0] = PPM_SC_TIMERFD_CREATE, [__NR_eventfd - SYSCALL_TABLE_ID0] = PPM_SC_EVENTFD, [__NR_timerfd_settime - SYSCALL_TABLE_ID0] = PPM_SC_TIMERFD_SETTIME, [__NR_timerfd_gettime - SYSCALL_TABLE_ID0] = PPM_SC_TIMERFD_GETTIME, [__NR_signalfd4 - SYSCALL_TABLE_ID0] = PPM_SC_SIGNALFD4, [__NR_eventfd2 - SYSCALL_TABLE_ID0] = PPM_SC_EVENTFD2, [__NR_epoll_create1 - SYSCALL_TABLE_ID0] = PPM_SC_EPOLL_CREATE1, [__NR_dup3 - SYSCALL_TABLE_ID0] = PPM_SC_DUP3, [__NR_pipe2 - SYSCALL_TABLE_ID0] = PPM_SC_PIPE2, [__NR_inotify_init1 - SYSCALL_TABLE_ID0] = PPM_SC_INOTIFY_INIT1, [__NR_preadv - SYSCALL_TABLE_ID0] = PPM_SC_PREADV, [__NR_pwritev - SYSCALL_TABLE_ID0] = PPM_SC_PWRITEV, [__NR_rt_tgsigqueueinfo - SYSCALL_TABLE_ID0] = PPM_SC_RT_TGSIGQUEUEINFO, [__NR_perf_event_open - SYSCALL_TABLE_ID0] = PPM_SC_PERF_EVENT_OPEN, #ifdef __NR_fanotify_init [__NR_fanotify_init - SYSCALL_TABLE_ID0] = PPM_SC_FANOTIFY_INIT, #endif #ifdef __NR_prlimit64 [__NR_prlimit64 - SYSCALL_TABLE_ID0] = PPM_SC_PRLIMIT64, #endif #ifdef __NR_clock_adjtime [__NR_clock_adjtime - SYSCALL_TABLE_ID0] = PPM_SC_CLOCK_ADJTIME, #endif #ifdef __NR_syncfs [__NR_syncfs - SYSCALL_TABLE_ID0] = PPM_SC_SYNCFS, #endif [__NR_getdents64 - SYSCALL_TABLE_ID0] = PPM_SC_GETDENTS64, #ifndef __NR_socketcall /* * Non-multiplexed socket family */ [__NR_socket - SYSCALL_TABLE_ID0] = PPM_SC_SOCKET, [__NR_bind - SYSCALL_TABLE_ID0] = PPM_SC_BIND, [__NR_connect - SYSCALL_TABLE_ID0] = PPM_SC_CONNECT, [__NR_listen - SYSCALL_TABLE_ID0] = PPM_SC_LISTEN, [__NR_accept - SYSCALL_TABLE_ID0] = PPM_SC_ACCEPT, [__NR_getsockname - SYSCALL_TABLE_ID0] = PPM_SC_GETSOCKNAME, [__NR_getpeername - SYSCALL_TABLE_ID0] = PPM_SC_GETPEERNAME, [__NR_socketpair - SYSCALL_TABLE_ID0] = PPM_SC_SOCKETPAIR, /* [__NR_send - SYSCALL_TABLE_ID0] = PPM_SC_NR_SEND, */ [__NR_sendto - SYSCALL_TABLE_ID0] = PPM_SC_SENDTO, /* [__NR_recv - SYSCALL_TABLE_ID0] = PPM_SC_NR_RECV, */ [__NR_recvfrom - SYSCALL_TABLE_ID0] = PPM_SC_RECVFROM, [__NR_shutdown - SYSCALL_TABLE_ID0] = PPM_SC_SHUTDOWN, [__NR_setsockopt - SYSCALL_TABLE_ID0] = PPM_SC_SETSOCKOPT, [__NR_getsockopt - SYSCALL_TABLE_ID0] = PPM_SC_GETSOCKOPT, [__NR_sendmsg - SYSCALL_TABLE_ID0] = PPM_SC_SENDMSG, [__NR_recvmsg - SYSCALL_TABLE_ID0] = PPM_SC_RECVMSG, [__NR_accept4 - SYSCALL_TABLE_ID0] = PPM_SC_ACCEPT4, #else [__NR_socketcall - SYSCALL_TABLE_ID0] = PPM_SC_SOCKETCALL, #endif #ifdef __NR_sendmmsg [__NR_sendmmsg - SYSCALL_TABLE_ID0] = PPM_SC_SENDMMSG, #endif #ifdef __NR_recvmmsg [__NR_recvmmsg - SYSCALL_TABLE_ID0] = PPM_SC_RECVMMSG, #endif /* * Non-multiplexed IPC family */ #ifdef __NR_semop [__NR_semop - SYSCALL_TABLE_ID0] = PPM_SC_SEMOP, #endif #ifdef __NR_semget [__NR_semget - SYSCALL_TABLE_ID0] = PPM_SC_SEMGET, #endif #ifdef __NR_semctl [__NR_semctl - SYSCALL_TABLE_ID0] = PPM_SC_SEMCTL, #endif #ifdef __NR_msgsnd [__NR_msgsnd - SYSCALL_TABLE_ID0] = PPM_SC_MSGSND, #endif #ifdef __NR_msgrcv [__NR_msgrcv - SYSCALL_TABLE_ID0] = PPM_SC_MSGRCV, #endif #ifdef __NR_msgget [__NR_msgget - SYSCALL_TABLE_ID0] = PPM_SC_MSGGET, #endif #ifdef __NR_msgctl [__NR_msgctl - SYSCALL_TABLE_ID0] = PPM_SC_MSGCTL, #endif /* [__NR_shmatcall - SYSCALL_TABLE_ID0] = PPM_SC_NR_SHMATCALL, */ #ifdef __NR_shmdt [__NR_shmdt - SYSCALL_TABLE_ID0] = PPM_SC_SHMDT, #endif #ifdef __NR_shmget [__NR_shmget - SYSCALL_TABLE_ID0] = PPM_SC_SHMGET, #endif #ifdef __NR_shmctl [__NR_shmctl - SYSCALL_TABLE_ID0] = PPM_SC_SHMCTL, #endif /* [__NR_fcntl64 - SYSCALL_TABLE_ID0] = PPM_SC_NR_FCNTL64, */ #ifdef __NR_statfs64 [__NR_statfs64 - SYSCALL_TABLE_ID0] = PPM_SC_STATFS64, #endif #ifdef __NR_fstatfs64 [__NR_fstatfs64 - SYSCALL_TABLE_ID0] = PPM_SC_FSTATFS64, #endif #ifdef __NR_fstatat64 [__NR_fstatat64 - SYSCALL_TABLE_ID0] = PPM_SC_FSTATAT64, #endif #ifdef __NR_sendfile64 [__NR_sendfile64 - SYSCALL_TABLE_ID0] = PPM_SC_SENDFILE64, #endif #ifdef __NR_ugetrlimit [__NR_ugetrlimit - SYSCALL_TABLE_ID0] = PPM_SC_UGETRLIMIT, #endif #ifdef __NR_bdflush [__NR_bdflush - SYSCALL_TABLE_ID0] = PPM_SC_BDFLUSH, #endif #ifdef __NR_sigprocmask [__NR_sigprocmask - SYSCALL_TABLE_ID0] = PPM_SC_SIGPROCMASK, #endif #ifdef __NR_ipc [__NR_ipc - SYSCALL_TABLE_ID0] = PPM_SC_IPC, #endif #ifdef __NR_stat64 [__NR_stat64 - SYSCALL_TABLE_ID0] = PPM_SC_STAT64, #endif #ifdef __NR_lstat64 [__NR_lstat64 - SYSCALL_TABLE_ID0] = PPM_SC_LSTAT64, #endif #ifdef __NR_fstat64 [__NR_fstat64 - SYSCALL_TABLE_ID0] = PPM_SC_FSTAT64, #endif #ifdef __NR_fcntl64 [__NR_fcntl64 - SYSCALL_TABLE_ID0] = PPM_SC_FCNTL64, #endif #ifdef __NR_mmap2 [__NR_mmap2 - SYSCALL_TABLE_ID0] = PPM_SC_MMAP2, #endif #ifdef __NR__newselect [__NR__newselect - SYSCALL_TABLE_ID0] = PPM_SC__NEWSELECT, #endif #ifdef __NR_sgetmask [__NR_sgetmask - SYSCALL_TABLE_ID0] = PPM_SC_SGETMASK, #endif #ifdef __NR_ssetmask [__NR_ssetmask - SYSCALL_TABLE_ID0] = PPM_SC_SSETMASK, #endif /* [__NR_setreuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETREUID16, */ /* [__NR_setregid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETREGID16, */ #ifdef __NR_sigpending [__NR_sigpending - SYSCALL_TABLE_ID0] = PPM_SC_SIGPENDING, #endif #ifdef __NR_olduname [__NR_olduname - SYSCALL_TABLE_ID0] = PPM_SC_OLDUNAME, #endif #ifdef __NR_umount [__NR_umount - SYSCALL_TABLE_ID0] = PPM_SC_UMOUNT, #endif #ifdef __NR_signal [__NR_signal - SYSCALL_TABLE_ID0] = PPM_SC_SIGNAL, #endif #ifdef __NR_nice [__NR_nice - SYSCALL_TABLE_ID0] = PPM_SC_NICE, #endif #ifdef __NR_stime [__NR_stime - SYSCALL_TABLE_ID0] = PPM_SC_STIME, #endif #ifdef __NR__llseek [__NR__llseek - SYSCALL_TABLE_ID0] = PPM_SC__LLSEEK, #endif #ifdef __NR_waitpid [__NR_waitpid - SYSCALL_TABLE_ID0] = PPM_SC_WAITPID, #endif #ifdef __NR_pread64 [__NR_pread64 - SYSCALL_TABLE_ID0] = PPM_SC_PREAD64, #endif #ifdef __NR_pwrite64 [__NR_pwrite64 - SYSCALL_TABLE_ID0] = PPM_SC_PWRITE64, #endif #ifdef __NR_shmat [__NR_shmat - SYSCALL_TABLE_ID0] = PPM_SC_SHMAT, #endif #ifdef __NR_rt_sigreturn [__NR_rt_sigreturn - SYSCALL_TABLE_ID0] = PPM_SC_SIGRETURN, #endif #ifdef __NR_fallocate [__NR_fallocate - SYSCALL_TABLE_ID0] = PPM_SC_FALLOCATE, #endif #ifdef __NR_newfstatat [__NR_newfstatat - SYSCALL_TABLE_ID0] = PPM_SC_NEWFSSTAT, #endif #ifdef __NR_process_vm_readv [__NR_process_vm_readv - SYSCALL_TABLE_ID0] = PPM_SC_PROCESS_VM_READV, #endif #ifdef __NR_process_vm_writev [__NR_process_vm_writev - SYSCALL_TABLE_ID0] = PPM_SC_PROCESS_VM_WRITEV, #endif #ifdef __NR_fork [__NR_fork - SYSCALL_TABLE_ID0] = PPM_SC_FORK, #endif #ifdef __NR_vfork [__NR_vfork - SYSCALL_TABLE_ID0] = PPM_SC_VFORK, #endif #ifdef __NR_quotactl [__NR_quotactl - SYSCALL_TABLE_ID0] = PPM_SC_QUOTACTL, #endif #ifdef __NR_setresuid [__NR_setresuid - SYSCALL_TABLE_ID0] = PPM_SC_SETRESUID, #endif #ifdef __NR_setresuid32 [__NR_setresuid32 - SYSCALL_TABLE_ID0] = PPM_SC_SETRESUID, #endif #ifdef __NR_setresgid [__NR_setresgid - SYSCALL_TABLE_ID0] = PPM_SC_SETRESGID, #endif #ifdef __NR_setresgid32 [__NR_setresgid32 - SYSCALL_TABLE_ID0] = PPM_SC_SETRESGID, #endif #ifdef __NR_setuid [__NR_setuid - SYSCALL_TABLE_ID0] = PPM_SC_SETUID, #endif #ifdef __NR_setuid32 [__NR_setuid32 - SYSCALL_TABLE_ID0] = PPM_SC_SETUID32, #endif #ifdef __NR_setgid [__NR_setgid - SYSCALL_TABLE_ID0] = PPM_SC_SETGID, #endif #ifdef __NR_setgid32 [__NR_setgid32 - SYSCALL_TABLE_ID0] = PPM_SC_SETGID32, #endif #ifdef __NR_getuid [__NR_getuid - SYSCALL_TABLE_ID0] = PPM_SC_GETUID, #endif #ifdef __NR_getuid32 [__NR_getuid32 - SYSCALL_TABLE_ID0] = PPM_SC_GETUID32, #endif #ifdef __NR_geteuid [__NR_geteuid - SYSCALL_TABLE_ID0] = PPM_SC_GETEUID, #endif #ifdef __NR_geteuid32 [__NR_geteuid32 - SYSCALL_TABLE_ID0] = PPM_SC_GETEUID, #endif #ifdef __NR_getgid [__NR_getgid - SYSCALL_TABLE_ID0] = PPM_SC_GETGID, #endif #ifdef __NR_getgid32 [__NR_getgid32 - SYSCALL_TABLE_ID0] = PPM_SC_GETGID, #endif #ifdef __NR_getegid [__NR_getegid - SYSCALL_TABLE_ID0] = PPM_SC_GETEGID, #endif #ifdef __NR_getegid32 [__NR_getegid32 - SYSCALL_TABLE_ID0] = PPM_SC_GETEGID, #endif #ifdef __NR_getresuid [__NR_getresuid - SYSCALL_TABLE_ID0] = PPM_SC_GETRESUID, #endif #ifdef __NR_getresuid32 [__NR_getresuid32 - SYSCALL_TABLE_ID0] = PPM_SC_GETRESUID32, #endif #ifdef __NR_getresgid [__NR_getresgid - SYSCALL_TABLE_ID0] = PPM_SC_GETRESGID, #endif #ifdef __NR_getresgid32 [__NR_getresgid32 - SYSCALL_TABLE_ID0] = PPM_SC_GETRESGID32, #endif #ifdef __NR_setns [__NR_setns - SYSCALL_TABLE_ID0] = PPM_SC_SETNS, #endif #ifdef __NR_access [__NR_access - SYSCALL_TABLE_ID0] = PPM_SC_ACCESS, #endif #ifdef __NR_finit_module [__NR_finit_module - SYSCALL_TABLE_ID0] = PPM_SC_FINIT_MODULE, #endif #ifdef __NR_bpf [__NR_bpf - SYSCALL_TABLE_ID0] = PPM_SC_BPF, #endif #ifdef __NR_seccomp [__NR_seccomp - SYSCALL_TABLE_ID0] = PPM_SC_SECCOMP, #endif #ifdef __NR_sigaltstack [__NR_sigaltstack - SYSCALL_TABLE_ID0] = PPM_SC_SIGALTSTACK, #endif #ifdef __NR_getrandom [__NR_getrandom - SYSCALL_TABLE_ID0] = PPM_SC_GETRANDOM, #endif #ifdef __NR_fadvise64 [__NR_fadvise64 - SYSCALL_TABLE_ID0] = PPM_SC_FADVISE64, #endif }; #ifdef CONFIG_IA32_EMULATION const struct syscall_evt_pair g_syscall_ia32_table[SYSCALL_TABLE_SIZE] = { [__NR_ia32_open - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_OPEN_E, PPME_SYSCALL_OPEN_X}, [__NR_ia32_creat - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_CREAT_E, PPME_SYSCALL_CREAT_X}, [__NR_ia32_close - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_CLOSE_E, PPME_SYSCALL_CLOSE_X}, [__NR_ia32_brk - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_BRK_4_E, PPME_SYSCALL_BRK_4_X}, [__NR_ia32_read - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_READ_E, PPME_SYSCALL_READ_X}, [__NR_ia32_write - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_WRITE_E, PPME_SYSCALL_WRITE_X}, [__NR_ia32_execve - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_EXECVE_19_E, PPME_SYSCALL_EXECVE_19_X}, [__NR_ia32_clone - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_CLONE_20_E, PPME_SYSCALL_CLONE_20_X}, [__NR_ia32_fork - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_FORK_20_E, PPME_SYSCALL_FORK_20_X}, [__NR_ia32_vfork - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_VFORK_20_E, PPME_SYSCALL_VFORK_20_X}, [__NR_ia32_pipe - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_PIPE_E, PPME_SYSCALL_PIPE_X}, [__NR_ia32_pipe2 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_PIPE_E, PPME_SYSCALL_PIPE_X}, [__NR_ia32_eventfd - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_EVENTFD_E, PPME_SYSCALL_EVENTFD_X}, [__NR_ia32_eventfd2 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_EVENTFD_E, PPME_SYSCALL_EVENTFD_X}, [__NR_ia32_futex - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_FUTEX_E, PPME_SYSCALL_FUTEX_X}, [__NR_ia32_stat - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_STAT_E, PPME_SYSCALL_STAT_X}, [__NR_ia32_lstat - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_LSTAT_E, PPME_SYSCALL_LSTAT_X}, [__NR_ia32_fstat - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_FSTAT_E, PPME_SYSCALL_FSTAT_X}, [__NR_ia32_epoll_wait - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_EPOLLWAIT_E, PPME_SYSCALL_EPOLLWAIT_X}, [__NR_ia32_poll - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_POLL_E, PPME_SYSCALL_POLL_X}, #ifdef __NR_ia32_select [__NR_ia32_select - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_SELECT_E, PPME_SYSCALL_SELECT_X}, #endif [__NR_ia32_lseek - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_LSEEK_E, PPME_SYSCALL_LSEEK_X}, [__NR_ia32_ioctl - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_IOCTL_3_E, PPME_SYSCALL_IOCTL_3_X}, [__NR_ia32_getcwd - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_GETCWD_E, PPME_SYSCALL_GETCWD_X}, [__NR_ia32_chdir - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_CHDIR_E, PPME_SYSCALL_CHDIR_X}, [__NR_ia32_fchdir - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_FCHDIR_E, PPME_SYSCALL_FCHDIR_X}, [__NR_ia32_mkdir - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_MKDIR_2_E, PPME_SYSCALL_MKDIR_2_X}, [__NR_ia32_rmdir - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_RMDIR_2_E, PPME_SYSCALL_RMDIR_2_X}, [__NR_ia32_openat - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_OPENAT_2_E, PPME_SYSCALL_OPENAT_2_X}, [__NR_ia32_mkdirat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_MKDIRAT_E, PPME_SYSCALL_MKDIRAT_X}, [__NR_ia32_link - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_LINK_2_E, PPME_SYSCALL_LINK_2_X}, [__NR_ia32_linkat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_LINKAT_2_E, PPME_SYSCALL_LINKAT_2_X}, [__NR_ia32_unlink - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_UNLINK_2_E, PPME_SYSCALL_UNLINK_2_X}, [__NR_ia32_unlinkat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_UNLINKAT_2_E, PPME_SYSCALL_UNLINKAT_2_X}, [__NR_ia32_pread64 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_PREAD_E, PPME_SYSCALL_PREAD_X}, [__NR_ia32_pwrite64 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_PWRITE_E, PPME_SYSCALL_PWRITE_X}, [__NR_ia32_readv - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_READV_E, PPME_SYSCALL_READV_X}, [__NR_ia32_writev - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_WRITEV_E, PPME_SYSCALL_WRITEV_X}, [__NR_ia32_preadv - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_PREADV_E, PPME_SYSCALL_PREADV_X}, [__NR_ia32_pwritev - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_PWRITEV_E, PPME_SYSCALL_PWRITEV_X}, [__NR_ia32_dup - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_DUP_E, PPME_SYSCALL_DUP_X}, [__NR_ia32_dup2 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_DUP_E, PPME_SYSCALL_DUP_X}, [__NR_ia32_dup3 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP | UF_SIMPLEDRIVER_KEEP, PPME_SYSCALL_DUP_E, PPME_SYSCALL_DUP_X}, [__NR_ia32_signalfd - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_SIGNALFD_E, PPME_SYSCALL_SIGNALFD_X}, [__NR_ia32_signalfd4 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_SIGNALFD_E, PPME_SYSCALL_SIGNALFD_X}, [__NR_ia32_kill - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_KILL_E, PPME_SYSCALL_KILL_X}, [__NR_ia32_tkill - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_TKILL_E, PPME_SYSCALL_TKILL_X}, [__NR_ia32_tgkill - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_TGKILL_E, PPME_SYSCALL_TGKILL_X}, [__NR_ia32_nanosleep - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_NANOSLEEP_E, PPME_SYSCALL_NANOSLEEP_X}, [__NR_ia32_timerfd_create - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_TIMERFD_CREATE_E, PPME_SYSCALL_TIMERFD_CREATE_X}, [__NR_ia32_inotify_init - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_INOTIFY_INIT_E, PPME_SYSCALL_INOTIFY_INIT_X}, [__NR_ia32_inotify_init1 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_INOTIFY_INIT_E, PPME_SYSCALL_INOTIFY_INIT_X}, [__NR_ia32_getrlimit - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_GETRLIMIT_E, PPME_SYSCALL_GETRLIMIT_X}, [__NR_ia32_setrlimit - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_SETRLIMIT_E, PPME_SYSCALL_SETRLIMIT_X}, [__NR_ia32_fchmodat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_FCHMODAT_E, PPME_SYSCALL_FCHMODAT_X}, [__NR_ia32_fchmod - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_FCHMOD_E, PPME_SYSCALL_FCHMOD_X}, #ifdef __NR_ia32_prlimit64 [__NR_ia32_prlimit64 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_PRLIMIT_E, PPME_SYSCALL_PRLIMIT_X}, #endif #ifdef __NR_ia32_ugetrlimit [__NR_ia32_ugetrlimit - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_GETRLIMIT_E, PPME_SYSCALL_GETRLIMIT_X}, #endif [__NR_ia32_fcntl - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_FCNTL_E, PPME_SYSCALL_FCNTL_X}, #ifdef __NR_ia32_fcntl64 [__NR_ia32_fcntl64 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_FCNTL_E, PPME_SYSCALL_FCNTL_X}, #endif [__NR_ia32_ppoll - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_PPOLL_E, PPME_SYSCALL_PPOLL_X}, /* [__NR_ia32_old_select - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, */ [__NR_ia32_pselect6 - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_epoll_create - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_epoll_ctl - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_uselib - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_sched_setparam - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_sched_getparam - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_syslog - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_chmod - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_CHMOD_E, PPME_SYSCALL_CHMOD_X}, [__NR_ia32_lchown - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_utime - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_mount - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_umount2 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_ptrace - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_PTRACE_E, PPME_SYSCALL_PTRACE_X}, [__NR_ia32_alarm - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_pause - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, #ifndef __NR_ia32_socketcall [__NR_ia32_socket - SYSCALL_TABLE_ID0] = {UF_USED | UF_SIMPLEDRIVER_KEEP, PPME_SOCKET_SOCKET_E, PPME_SOCKET_SOCKET_X}, [__NR_ia32_bind - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_BIND_E, PPME_SOCKET_BIND_X}, [__NR_ia32_connect - SYSCALL_TABLE_ID0] = {UF_USED | UF_SIMPLEDRIVER_KEEP, PPME_SOCKET_CONNECT_E, PPME_SOCKET_CONNECT_X}, [__NR_ia32_listen - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_LISTEN_E, PPME_SOCKET_LISTEN_X}, [__NR_ia32_accept - SYSCALL_TABLE_ID0] = {UF_USED | UF_SIMPLEDRIVER_KEEP, PPME_SOCKET_ACCEPT_E, PPME_SOCKET_ACCEPT_X}, [__NR_ia32_getsockname - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SOCKET_GETSOCKNAME_E, PPME_SOCKET_GETSOCKNAME_X}, [__NR_ia32_getpeername - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SOCKET_GETPEERNAME_E, PPME_SOCKET_GETPEERNAME_X}, [__NR_ia32_socketpair - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SOCKET_SOCKETPAIR_E, PPME_SOCKET_SOCKETPAIR_X}, [__NR_ia32_sendto - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_SENDTO_E, PPME_SOCKET_SENDTO_X}, [__NR_ia32_recvfrom - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_RECVFROM_E, PPME_SOCKET_RECVFROM_X}, [__NR_ia32_shutdown - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_SHUTDOWN_E, PPME_SOCKET_SHUTDOWN_X}, [__NR_ia32_setsockopt - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SOCKET_SETSOCKOPT_E, PPME_SOCKET_SETSOCKOPT_X}, [__NR_ia32_getsockopt - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_GETSOCKOPT_E, PPME_SOCKET_GETSOCKOPT_X}, [__NR_ia32_sendmsg - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_SENDMSG_E, PPME_SOCKET_SENDMSG_X}, [__NR_ia32_accept4 - SYSCALL_TABLE_ID0] = {UF_USED | UF_SIMPLEDRIVER_KEEP, PPME_SOCKET_ACCEPT4_E, PPME_SOCKET_ACCEPT4_X}, #endif #ifdef __NR_ia32_sendmmsg [__NR_ia32_sendmmsg - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_SENDMMSG_E, PPME_SOCKET_SENDMMSG_X}, #endif #ifdef __NR_ia32_recvmsg [__NR_ia32_recvmsg - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_RECVMSG_E, PPME_SOCKET_RECVMSG_X}, #endif #ifdef __NR_ia32_recvmmsg [__NR_ia32_recvmmsg - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_RECVMMSG_E, PPME_SOCKET_RECVMMSG_X}, #endif #ifdef __NR_ia32_stat64 [__NR_ia32_stat64 - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_STAT64_E, PPME_SYSCALL_STAT64_X}, #endif #ifdef __NR_ia32_fstat64 [__NR_ia32_fstat64 - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_FSTAT64_E, PPME_SYSCALL_FSTAT64_X}, #endif #ifdef __NR_ia32__llseek [__NR_ia32__llseek - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_LLSEEK_E, PPME_SYSCALL_LLSEEK_X}, #endif [__NR_ia32_mmap - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_MMAP_E, PPME_SYSCALL_MMAP_X}, #ifdef __NR_ia32_mmap2 [__NR_ia32_mmap2 - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_MMAP2_E, PPME_SYSCALL_MMAP2_X}, #endif [__NR_ia32_munmap - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_MUNMAP_E, PPME_SYSCALL_MUNMAP_X}, [__NR_ia32_splice - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SPLICE_E, PPME_SYSCALL_SPLICE_X}, #ifdef __NR_ia32_process_vm_readv [__NR_ia32_process_vm_readv - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, #endif #ifdef __NR_ia32_process_vm_writev [__NR_ia32_process_vm_writev - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, #endif [__NR_ia32_rename - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_RENAME_E, PPME_SYSCALL_RENAME_X}, [__NR_ia32_renameat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_RENAMEAT_E, PPME_SYSCALL_RENAMEAT_X}, [__NR_ia32_symlink - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SYMLINK_E, PPME_SYSCALL_SYMLINK_X}, [__NR_ia32_symlinkat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SYMLINKAT_E, PPME_SYSCALL_SYMLINKAT_X}, [__NR_ia32_sendfile - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SENDFILE_E, PPME_SYSCALL_SENDFILE_X}, #ifdef __NR_ia32_sendfile64 [__NR_ia32_sendfile64 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SENDFILE_E, PPME_SYSCALL_SENDFILE_X}, #endif #ifdef __NR_ia32_quotactl [__NR_ia32_quotactl - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_QUOTACTL_E, PPME_SYSCALL_QUOTACTL_X}, #endif #ifdef __NR_ia32_setresuid [__NR_ia32_setresuid - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SETRESUID_E, PPME_SYSCALL_SETRESUID_X }, #endif #ifdef __NR_ia32_setresuid32 [__NR_ia32_setresuid32 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SETRESUID_E, PPME_SYSCALL_SETRESUID_X }, #endif #ifdef __NR_ia32_setresgid [__NR_ia32_setresgid - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SETRESGID_E, PPME_SYSCALL_SETRESGID_X }, #endif #ifdef __NR_ia32_setresgid32 [__NR_ia32_setresgid32 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SETRESGID_E, PPME_SYSCALL_SETRESGID_X }, #endif #ifdef __NR_ia32_setuid [__NR_ia32_setuid - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_SETUID_E, PPME_SYSCALL_SETUID_X }, #endif #ifdef __NR_ia32_setuid32 [__NR_ia32_setuid32 - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_SETUID_E, PPME_SYSCALL_SETUID_X }, #endif #ifdef __NR_ia32_setgid [__NR_ia32_setgid - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_SETGID_E, PPME_SYSCALL_SETGID_X }, #endif #ifdef __NR_ia32_setgid32 [__NR_ia32_setgid32 - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_SETGID_E, PPME_SYSCALL_SETGID_X }, #endif #ifdef __NR_ia32_getuid [__NR_ia32_getuid - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_GETUID_E, PPME_SYSCALL_GETUID_X }, #endif #ifdef __NR_ia32_getuid32 [__NR_ia32_getuid32 - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_GETUID_E, PPME_SYSCALL_GETUID_X }, #endif #ifdef __NR_ia32_geteuid [__NR_ia32_geteuid - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_GETEUID_E, PPME_SYSCALL_GETEUID_X }, #endif #ifdef __NR_ia32_geteuid32 [__NR_ia32_geteuid32 - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_GETEUID_E, PPME_SYSCALL_GETEUID_X }, #endif #ifdef __NR_ia32_getgid [__NR_ia32_getgid - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_GETGID_E, PPME_SYSCALL_GETGID_X }, #endif #ifdef __NR_ia32_getgid32 [__NR_ia32_getgid32 - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_GETGID_E, PPME_SYSCALL_GETGID_X }, #endif #ifdef __NR_ia32_getegid [__NR_ia32_getegid - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_GETEGID_E, PPME_SYSCALL_GETEGID_X }, #endif #ifdef __NR_ia32_getegid32 [__NR_ia32_getegid32 - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_GETEGID_E, PPME_SYSCALL_GETEGID_X }, #endif #ifdef __NR_ia32_getresuid [__NR_ia32_getresuid - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_GETRESUID_E, PPME_SYSCALL_GETRESUID_X }, #endif #ifdef __NR_ia32_getresuid32 [__NR_ia32_getresuid32 - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_GETRESUID_E, PPME_SYSCALL_GETRESUID_X }, #endif #ifdef __NR_ia32_getresgid [__NR_ia32_getresgid - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_GETRESGID_E, PPME_SYSCALL_GETRESGID_X }, #endif #ifdef __NR_ia32_getresgid32 [__NR_ia32_getresgid32 - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_GETRESGID_E, PPME_SYSCALL_GETRESGID_X }, #endif #ifdef __NR_ia32_semop [__NR_ia32_semop - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_SEMOP_E, PPME_SYSCALL_SEMOP_X}, #endif #ifdef __NR_ia32_semget [__NR_ia32_semget - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_SEMGET_E, PPME_SYSCALL_SEMGET_X}, #endif #ifdef __NR_ia32_semctl [__NR_ia32_semctl - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_SEMCTL_E, PPME_SYSCALL_SEMCTL_X}, #endif #ifdef __NR_ia32_access [__NR_ia32_access - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_ACCESS_E, PPME_SYSCALL_ACCESS_X}, #endif #ifdef __NR_ia32_chroot [__NR_ia32_chroot - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_CHROOT_E, PPME_SYSCALL_CHROOT_X}, #endif [__NR_ia32_setsid - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_SETSID_E, PPME_SYSCALL_SETSID_X}, [__NR_ia32_setpgid - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_SETPGID_E, PPME_SYSCALL_SETPGID_X}, #ifdef __NR_ia32_bpf [__NR_ia32_bpf - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_BPF_E, PPME_SYSCALL_BPF_X}, #endif #ifdef __NR_ia32_seccomp [__NR_ia32_seccomp - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SECCOMP_E, PPME_SYSCALL_SECCOMP_X}, #endif }; /* * SYSCALL ROUTING TABLE */ const enum ppm_syscall_code g_syscall_ia32_code_routing_table[SYSCALL_TABLE_SIZE] = { [__NR_ia32_restart_syscall - SYSCALL_TABLE_ID0] = PPM_SC_RESTART_SYSCALL, [__NR_ia32_exit - SYSCALL_TABLE_ID0] = PPM_SC_EXIT, [__NR_ia32_read - SYSCALL_TABLE_ID0] = PPM_SC_READ, [__NR_ia32_write - SYSCALL_TABLE_ID0] = PPM_SC_WRITE, [__NR_ia32_open - SYSCALL_TABLE_ID0] = PPM_SC_OPEN, [__NR_ia32_close - SYSCALL_TABLE_ID0] = PPM_SC_CLOSE, [__NR_ia32_creat - SYSCALL_TABLE_ID0] = PPM_SC_CREAT, [__NR_ia32_link - SYSCALL_TABLE_ID0] = PPM_SC_LINK, [__NR_ia32_unlink - SYSCALL_TABLE_ID0] = PPM_SC_UNLINK, [__NR_ia32_chdir - SYSCALL_TABLE_ID0] = PPM_SC_CHDIR, [__NR_ia32_time - SYSCALL_TABLE_ID0] = PPM_SC_TIME, [__NR_ia32_mknod - SYSCALL_TABLE_ID0] = PPM_SC_MKNOD, [__NR_ia32_chmod - SYSCALL_TABLE_ID0] = PPM_SC_CHMOD, /* [__NR_ia32_lchown16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_LCHOWN16, */ [__NR_ia32_stat - SYSCALL_TABLE_ID0] = PPM_SC_STAT, [__NR_ia32_lseek - SYSCALL_TABLE_ID0] = PPM_SC_LSEEK, [__NR_ia32_getpid - SYSCALL_TABLE_ID0] = PPM_SC_GETPID, [__NR_ia32_mount - SYSCALL_TABLE_ID0] = PPM_SC_MOUNT, /* [__NR_ia32_oldumount - SYSCALL_TABLE_ID0] = PPM_SC_NR_OLDUMOUNT, */ /* [__NR_ia32_setuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETUID16, */ /* [__NR_ia32_getuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETUID16, */ [__NR_ia32_ptrace - SYSCALL_TABLE_ID0] = PPM_SC_PTRACE, [__NR_ia32_alarm - SYSCALL_TABLE_ID0] = PPM_SC_ALARM, [__NR_ia32_fstat - SYSCALL_TABLE_ID0] = PPM_SC_FSTAT, [__NR_ia32_pause - SYSCALL_TABLE_ID0] = PPM_SC_PAUSE, [__NR_ia32_utime - SYSCALL_TABLE_ID0] = PPM_SC_UTIME, [__NR_ia32_access - SYSCALL_TABLE_ID0] = PPM_SC_ACCESS, [__NR_ia32_sync - SYSCALL_TABLE_ID0] = PPM_SC_SYNC, [__NR_ia32_kill - SYSCALL_TABLE_ID0] = PPM_SC_KILL, [__NR_ia32_rename - SYSCALL_TABLE_ID0] = PPM_SC_RENAME, [__NR_ia32_mkdir - SYSCALL_TABLE_ID0] = PPM_SC_MKDIR, [__NR_ia32_rmdir - SYSCALL_TABLE_ID0] = PPM_SC_RMDIR, [__NR_ia32_dup - SYSCALL_TABLE_ID0] = PPM_SC_DUP, [__NR_ia32_pipe - SYSCALL_TABLE_ID0] = PPM_SC_PIPE, [__NR_ia32_times - SYSCALL_TABLE_ID0] = PPM_SC_TIMES, [__NR_ia32_brk - SYSCALL_TABLE_ID0] = PPM_SC_BRK, /* [__NR_ia32_setgid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETGID16, */ /* [__NR_ia32_getgid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETGID16, */ /* [__NR_ia32_geteuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETEUID16, */ /* [__NR_ia32_getegid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETEGID16, */ [__NR_ia32_acct - SYSCALL_TABLE_ID0] = PPM_SC_ACCT, [__NR_ia32_ioctl - SYSCALL_TABLE_ID0] = PPM_SC_IOCTL, [__NR_ia32_fcntl - SYSCALL_TABLE_ID0] = PPM_SC_FCNTL, [__NR_ia32_setpgid - SYSCALL_TABLE_ID0] = PPM_SC_SETPGID, [__NR_ia32_umask - SYSCALL_TABLE_ID0] = PPM_SC_UMASK, [__NR_ia32_chroot - SYSCALL_TABLE_ID0] = PPM_SC_CHROOT, [__NR_ia32_ustat - SYSCALL_TABLE_ID0] = PPM_SC_USTAT, [__NR_ia32_dup2 - SYSCALL_TABLE_ID0] = PPM_SC_DUP2, [__NR_ia32_getppid - SYSCALL_TABLE_ID0] = PPM_SC_GETPPID, [__NR_ia32_getpgrp - SYSCALL_TABLE_ID0] = PPM_SC_GETPGRP, [__NR_ia32_setsid - SYSCALL_TABLE_ID0] = PPM_SC_SETSID, [__NR_ia32_sethostname - SYSCALL_TABLE_ID0] = PPM_SC_SETHOSTNAME, [__NR_ia32_setrlimit - SYSCALL_TABLE_ID0] = PPM_SC_SETRLIMIT, /* [__NR_ia32_old_getrlimit - SYSCALL_TABLE_ID0] = PPM_SC_NR_OLD_GETRLIMIT, */ [__NR_ia32_getrusage - SYSCALL_TABLE_ID0] = PPM_SC_GETRUSAGE, [__NR_ia32_gettimeofday - SYSCALL_TABLE_ID0] = PPM_SC_GETTIMEOFDAY, [__NR_ia32_settimeofday - SYSCALL_TABLE_ID0] = PPM_SC_SETTIMEOFDAY, /* [__NR_ia32_getgroups16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETGROUPS16, */ /* [__NR_ia32_setgroups16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETGROUPS16, */ /* [__NR_ia32_old_select - SYSCALL_TABLE_ID0] = PPM_SC_NR_OLD_SELECT, */ [__NR_ia32_symlink - SYSCALL_TABLE_ID0] = PPM_SC_SYMLINK, [__NR_ia32_lstat - SYSCALL_TABLE_ID0] = PPM_SC_LSTAT, [__NR_ia32_readlink - SYSCALL_TABLE_ID0] = PPM_SC_READLINK, [__NR_ia32_uselib - SYSCALL_TABLE_ID0] = PPM_SC_USELIB, [__NR_ia32_swapon - SYSCALL_TABLE_ID0] = PPM_SC_SWAPON, [__NR_ia32_reboot - SYSCALL_TABLE_ID0] = PPM_SC_REBOOT, /* [__NR_ia32_old_readdir - SYSCALL_TABLE_ID0] = PPM_SC_NR_OLD_READDIR, */ /* [__NR_ia32_old_mmap - SYSCALL_TABLE_ID0] = PPM_SC_NR_OLD_MMAP, */ [__NR_ia32_mmap - SYSCALL_TABLE_ID0] = PPM_SC_MMAP, [__NR_ia32_munmap - SYSCALL_TABLE_ID0] = PPM_SC_MUNMAP, [__NR_ia32_truncate - SYSCALL_TABLE_ID0] = PPM_SC_TRUNCATE, [__NR_ia32_ftruncate - SYSCALL_TABLE_ID0] = PPM_SC_FTRUNCATE, [__NR_ia32_fchmod - SYSCALL_TABLE_ID0] = PPM_SC_FCHMOD, /* [__NR_ia32_fchown16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_FCHOWN16, */ [__NR_ia32_getpriority - SYSCALL_TABLE_ID0] = PPM_SC_GETPRIORITY, [__NR_ia32_setpriority - SYSCALL_TABLE_ID0] = PPM_SC_SETPRIORITY, [__NR_ia32_statfs - SYSCALL_TABLE_ID0] = PPM_SC_STATFS, [__NR_ia32_fstatfs - SYSCALL_TABLE_ID0] = PPM_SC_FSTATFS, [__NR_ia32_syslog - SYSCALL_TABLE_ID0] = PPM_SC_SYSLOG, [__NR_ia32_setitimer - SYSCALL_TABLE_ID0] = PPM_SC_SETITIMER, [__NR_ia32_getitimer - SYSCALL_TABLE_ID0] = PPM_SC_GETITIMER, /* [__NR_ia32_newstat - SYSCALL_TABLE_ID0] = PPM_SC_NR_NEWSTAT, */ /* [__NR_ia32_newlstat - SYSCALL_TABLE_ID0] = PPM_SC_NR_NEWLSTAT, */ /* [__NR_ia32_newfstat - SYSCALL_TABLE_ID0] = PPM_SC_NR_NEWFSTAT, */ [__NR_ia32_uname - SYSCALL_TABLE_ID0] = PPM_SC_UNAME, [__NR_ia32_vhangup - SYSCALL_TABLE_ID0] = PPM_SC_VHANGUP, [__NR_ia32_wait4 - SYSCALL_TABLE_ID0] = PPM_SC_WAIT4, [__NR_ia32_swapoff - SYSCALL_TABLE_ID0] = PPM_SC_SWAPOFF, [__NR_ia32_sysinfo - SYSCALL_TABLE_ID0] = PPM_SC_SYSINFO, [__NR_ia32_fsync - SYSCALL_TABLE_ID0] = PPM_SC_FSYNC, [__NR_ia32_setdomainname - SYSCALL_TABLE_ID0] = PPM_SC_SETDOMAINNAME, /* [__NR_ia32_newuname - SYSCALL_TABLE_ID0] = PPM_SC_NR_NEWUNAME, */ [__NR_ia32_adjtimex - SYSCALL_TABLE_ID0] = PPM_SC_ADJTIMEX, [__NR_ia32_mprotect - SYSCALL_TABLE_ID0] = PPM_SC_MPROTECT, [__NR_ia32_init_module - SYSCALL_TABLE_ID0] = PPM_SC_INIT_MODULE, [__NR_ia32_delete_module - SYSCALL_TABLE_ID0] = PPM_SC_DELETE_MODULE, [__NR_ia32_quotactl - SYSCALL_TABLE_ID0] = PPM_SC_QUOTACTL, [__NR_ia32_getpgid - SYSCALL_TABLE_ID0] = PPM_SC_GETPGID, [__NR_ia32_fchdir - SYSCALL_TABLE_ID0] = PPM_SC_FCHDIR, [__NR_ia32_sysfs - SYSCALL_TABLE_ID0] = PPM_SC_SYSFS, [__NR_ia32_personality - SYSCALL_TABLE_ID0] = PPM_SC_PERSONALITY, /* [__NR_ia32_setfsuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETFSUID16, */ /* [__NR_ia32_setfsgid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETFSGID16, */ /* [__NR_ia32_llseek - SYSCALL_TABLE_ID0] = PPM_SC_NR_LLSEEK, */ [__NR_ia32_getdents - SYSCALL_TABLE_ID0] = PPM_SC_GETDENTS, #ifdef __NR_ia32_select [__NR_ia32_select - SYSCALL_TABLE_ID0] = PPM_SC_SELECT, #endif [__NR_ia32_flock - SYSCALL_TABLE_ID0] = PPM_SC_FLOCK, [__NR_ia32_msync - SYSCALL_TABLE_ID0] = PPM_SC_MSYNC, [__NR_ia32_readv - SYSCALL_TABLE_ID0] = PPM_SC_READV, [__NR_ia32_writev - SYSCALL_TABLE_ID0] = PPM_SC_WRITEV, [__NR_ia32_getsid - SYSCALL_TABLE_ID0] = PPM_SC_GETSID, [__NR_ia32_fdatasync - SYSCALL_TABLE_ID0] = PPM_SC_FDATASYNC, /* [__NR_ia32_sysctl - SYSCALL_TABLE_ID0] = PPM_SC_NR_SYSCTL, */ [__NR_ia32_mlock - SYSCALL_TABLE_ID0] = PPM_SC_MLOCK, [__NR_ia32_munlock - SYSCALL_TABLE_ID0] = PPM_SC_MUNLOCK, [__NR_ia32_mlockall - SYSCALL_TABLE_ID0] = PPM_SC_MLOCKALL, [__NR_ia32_munlockall - SYSCALL_TABLE_ID0] = PPM_SC_MUNLOCKALL, [__NR_ia32_sched_setparam - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_SETPARAM, [__NR_ia32_sched_getparam - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_GETPARAM, [__NR_ia32_sched_setscheduler - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_SETSCHEDULER, [__NR_ia32_sched_getscheduler - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_GETSCHEDULER, [__NR_ia32_sched_yield - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_YIELD, [__NR_ia32_sched_get_priority_max - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_GET_PRIORITY_MAX, [__NR_ia32_sched_get_priority_min - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_GET_PRIORITY_MIN, [__NR_ia32_sched_rr_get_interval - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_RR_GET_INTERVAL, [__NR_ia32_nanosleep - SYSCALL_TABLE_ID0] = PPM_SC_NANOSLEEP, [__NR_ia32_mremap - SYSCALL_TABLE_ID0] = PPM_SC_MREMAP, /* [__NR_ia32_setresuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETRESUID16, */ /* [__NR_ia32_getresuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETRESUID16, */ [__NR_ia32_poll - SYSCALL_TABLE_ID0] = PPM_SC_POLL, /* [__NR_ia32_setresgid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETRESGID16, */ /* [__NR_ia32_getresgid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_GETRESGID16, */ [__NR_ia32_prctl - SYSCALL_TABLE_ID0] = PPM_SC_PRCTL, #ifdef __NR_ia32_arch_prctl [__NR_ia32_arch_prctl - SYSCALL_TABLE_ID0] = PPM_SC_ARCH_PRCTL, #endif [__NR_ia32_rt_sigaction - SYSCALL_TABLE_ID0] = PPM_SC_RT_SIGACTION, [__NR_ia32_rt_sigprocmask - SYSCALL_TABLE_ID0] = PPM_SC_RT_SIGPROCMASK, [__NR_ia32_rt_sigpending - SYSCALL_TABLE_ID0] = PPM_SC_RT_SIGPENDING, [__NR_ia32_rt_sigtimedwait - SYSCALL_TABLE_ID0] = PPM_SC_RT_SIGTIMEDWAIT, [__NR_ia32_rt_sigqueueinfo - SYSCALL_TABLE_ID0] = PPM_SC_RT_SIGQUEUEINFO, [__NR_ia32_rt_sigsuspend - SYSCALL_TABLE_ID0] = PPM_SC_RT_SIGSUSPEND, /* [__NR_ia32_chown16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_CHOWN16, */ [__NR_ia32_getcwd - SYSCALL_TABLE_ID0] = PPM_SC_GETCWD, [__NR_ia32_capget - SYSCALL_TABLE_ID0] = PPM_SC_CAPGET, [__NR_ia32_capset - SYSCALL_TABLE_ID0] = PPM_SC_CAPSET, [__NR_ia32_sendfile - SYSCALL_TABLE_ID0] = PPM_SC_SENDFILE, [__NR_ia32_getrlimit - SYSCALL_TABLE_ID0] = PPM_SC_GETRLIMIT, /* [__NR_ia32_mmap_pgoff - SYSCALL_TABLE_ID0] = PPM_SC_NR_MMAP_PGOFF, */ [__NR_ia32_lchown - SYSCALL_TABLE_ID0] = PPM_SC_LCHOWN, [__NR_ia32_getuid - SYSCALL_TABLE_ID0] = PPM_SC_GETUID, [__NR_ia32_getgid - SYSCALL_TABLE_ID0] = PPM_SC_GETGID, [__NR_ia32_geteuid - SYSCALL_TABLE_ID0] = PPM_SC_GETEUID, [__NR_ia32_getegid - SYSCALL_TABLE_ID0] = PPM_SC_GETEGID, [__NR_ia32_setreuid - SYSCALL_TABLE_ID0] = PPM_SC_SETREUID, [__NR_ia32_setregid - SYSCALL_TABLE_ID0] = PPM_SC_SETREGID, [__NR_ia32_getgroups - SYSCALL_TABLE_ID0] = PPM_SC_GETGROUPS, [__NR_ia32_setgroups - SYSCALL_TABLE_ID0] = PPM_SC_SETGROUPS, [__NR_ia32_fchown - SYSCALL_TABLE_ID0] = PPM_SC_FCHOWN, [__NR_ia32_setresuid - SYSCALL_TABLE_ID0] = PPM_SC_SETRESUID, [__NR_ia32_getresuid - SYSCALL_TABLE_ID0] = PPM_SC_GETRESUID, [__NR_ia32_setresgid - SYSCALL_TABLE_ID0] = PPM_SC_SETRESGID, [__NR_ia32_getresgid - SYSCALL_TABLE_ID0] = PPM_SC_GETRESGID, [__NR_ia32_chown - SYSCALL_TABLE_ID0] = PPM_SC_CHOWN, [__NR_ia32_setuid - SYSCALL_TABLE_ID0] = PPM_SC_SETUID, [__NR_ia32_setgid - SYSCALL_TABLE_ID0] = PPM_SC_SETGID, [__NR_ia32_setfsuid - SYSCALL_TABLE_ID0] = PPM_SC_SETFSUID, [__NR_ia32_setfsgid - SYSCALL_TABLE_ID0] = PPM_SC_SETFSGID, [__NR_ia32_pivot_root - SYSCALL_TABLE_ID0] = PPM_SC_PIVOT_ROOT, [__NR_ia32_mincore - SYSCALL_TABLE_ID0] = PPM_SC_MINCORE, [__NR_ia32_madvise - SYSCALL_TABLE_ID0] = PPM_SC_MADVISE, [__NR_ia32_gettid - SYSCALL_TABLE_ID0] = PPM_SC_GETTID, [__NR_ia32_setxattr - SYSCALL_TABLE_ID0] = PPM_SC_SETXATTR, [__NR_ia32_lsetxattr - SYSCALL_TABLE_ID0] = PPM_SC_LSETXATTR, [__NR_ia32_fsetxattr - SYSCALL_TABLE_ID0] = PPM_SC_FSETXATTR, [__NR_ia32_getxattr - SYSCALL_TABLE_ID0] = PPM_SC_GETXATTR, [__NR_ia32_lgetxattr - SYSCALL_TABLE_ID0] = PPM_SC_LGETXATTR, [__NR_ia32_fgetxattr - SYSCALL_TABLE_ID0] = PPM_SC_FGETXATTR, [__NR_ia32_listxattr - SYSCALL_TABLE_ID0] = PPM_SC_LISTXATTR, [__NR_ia32_llistxattr - SYSCALL_TABLE_ID0] = PPM_SC_LLISTXATTR, [__NR_ia32_flistxattr - SYSCALL_TABLE_ID0] = PPM_SC_FLISTXATTR, [__NR_ia32_removexattr - SYSCALL_TABLE_ID0] = PPM_SC_REMOVEXATTR, [__NR_ia32_lremovexattr - SYSCALL_TABLE_ID0] = PPM_SC_LREMOVEXATTR, [__NR_ia32_fremovexattr - SYSCALL_TABLE_ID0] = PPM_SC_FREMOVEXATTR, [__NR_ia32_tkill - SYSCALL_TABLE_ID0] = PPM_SC_TKILL, [__NR_ia32_futex - SYSCALL_TABLE_ID0] = PPM_SC_FUTEX, [__NR_ia32_sched_setaffinity - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_SETAFFINITY, [__NR_ia32_sched_getaffinity - SYSCALL_TABLE_ID0] = PPM_SC_SCHED_GETAFFINITY, #ifdef __NR_ia32_set_thread_area [__NR_ia32_set_thread_area - SYSCALL_TABLE_ID0] = PPM_SC_SET_THREAD_AREA, #endif #ifdef __NR_ia32_get_thread_area [__NR_ia32_get_thread_area - SYSCALL_TABLE_ID0] = PPM_SC_GET_THREAD_AREA, #endif [__NR_ia32_io_setup - SYSCALL_TABLE_ID0] = PPM_SC_IO_SETUP, [__NR_ia32_io_destroy - SYSCALL_TABLE_ID0] = PPM_SC_IO_DESTROY, [__NR_ia32_io_getevents - SYSCALL_TABLE_ID0] = PPM_SC_IO_GETEVENTS, [__NR_ia32_io_submit - SYSCALL_TABLE_ID0] = PPM_SC_IO_SUBMIT, [__NR_ia32_io_cancel - SYSCALL_TABLE_ID0] = PPM_SC_IO_CANCEL, [__NR_ia32_exit_group - SYSCALL_TABLE_ID0] = PPM_SC_EXIT_GROUP, [__NR_ia32_epoll_create - SYSCALL_TABLE_ID0] = PPM_SC_EPOLL_CREATE, [__NR_ia32_epoll_ctl - SYSCALL_TABLE_ID0] = PPM_SC_EPOLL_CTL, [__NR_ia32_epoll_wait - SYSCALL_TABLE_ID0] = PPM_SC_EPOLL_WAIT, [__NR_ia32_remap_file_pages - SYSCALL_TABLE_ID0] = PPM_SC_REMAP_FILE_PAGES, [__NR_ia32_set_tid_address - SYSCALL_TABLE_ID0] = PPM_SC_SET_TID_ADDRESS, [__NR_ia32_timer_create - SYSCALL_TABLE_ID0] = PPM_SC_TIMER_CREATE, [__NR_ia32_timer_settime - SYSCALL_TABLE_ID0] = PPM_SC_TIMER_SETTIME, [__NR_ia32_timer_gettime - SYSCALL_TABLE_ID0] = PPM_SC_TIMER_GETTIME, [__NR_ia32_timer_getoverrun - SYSCALL_TABLE_ID0] = PPM_SC_TIMER_GETOVERRUN, [__NR_ia32_timer_delete - SYSCALL_TABLE_ID0] = PPM_SC_TIMER_DELETE, [__NR_ia32_clock_settime - SYSCALL_TABLE_ID0] = PPM_SC_CLOCK_SETTIME, [__NR_ia32_clock_gettime - SYSCALL_TABLE_ID0] = PPM_SC_CLOCK_GETTIME, [__NR_ia32_clock_getres - SYSCALL_TABLE_ID0] = PPM_SC_CLOCK_GETRES, [__NR_ia32_clock_nanosleep - SYSCALL_TABLE_ID0] = PPM_SC_CLOCK_NANOSLEEP, [__NR_ia32_tgkill - SYSCALL_TABLE_ID0] = PPM_SC_TGKILL, [__NR_ia32_utimes - SYSCALL_TABLE_ID0] = PPM_SC_UTIMES, [__NR_ia32_mq_open - SYSCALL_TABLE_ID0] = PPM_SC_MQ_OPEN, [__NR_ia32_mq_unlink - SYSCALL_TABLE_ID0] = PPM_SC_MQ_UNLINK, [__NR_ia32_mq_timedsend - SYSCALL_TABLE_ID0] = PPM_SC_MQ_TIMEDSEND, [__NR_ia32_mq_timedreceive - SYSCALL_TABLE_ID0] = PPM_SC_MQ_TIMEDRECEIVE, [__NR_ia32_mq_notify - SYSCALL_TABLE_ID0] = PPM_SC_MQ_NOTIFY, [__NR_ia32_mq_getsetattr - SYSCALL_TABLE_ID0] = PPM_SC_MQ_GETSETATTR, [__NR_ia32_kexec_load - SYSCALL_TABLE_ID0] = PPM_SC_KEXEC_LOAD, [__NR_ia32_waitid - SYSCALL_TABLE_ID0] = PPM_SC_WAITID, [__NR_ia32_add_key - SYSCALL_TABLE_ID0] = PPM_SC_ADD_KEY, [__NR_ia32_request_key - SYSCALL_TABLE_ID0] = PPM_SC_REQUEST_KEY, [__NR_ia32_keyctl - SYSCALL_TABLE_ID0] = PPM_SC_KEYCTL, [__NR_ia32_ioprio_set - SYSCALL_TABLE_ID0] = PPM_SC_IOPRIO_SET, [__NR_ia32_ioprio_get - SYSCALL_TABLE_ID0] = PPM_SC_IOPRIO_GET, [__NR_ia32_inotify_init - SYSCALL_TABLE_ID0] = PPM_SC_INOTIFY_INIT, [__NR_ia32_inotify_add_watch - SYSCALL_TABLE_ID0] = PPM_SC_INOTIFY_ADD_WATCH, [__NR_ia32_inotify_rm_watch - SYSCALL_TABLE_ID0] = PPM_SC_INOTIFY_RM_WATCH, [__NR_ia32_openat - SYSCALL_TABLE_ID0] = PPM_SC_OPENAT, [__NR_ia32_mkdirat - SYSCALL_TABLE_ID0] = PPM_SC_MKDIRAT, [__NR_ia32_mknodat - SYSCALL_TABLE_ID0] = PPM_SC_MKNODAT, [__NR_ia32_fchownat - SYSCALL_TABLE_ID0] = PPM_SC_FCHOWNAT, [__NR_ia32_futimesat - SYSCALL_TABLE_ID0] = PPM_SC_FUTIMESAT, [__NR_ia32_unlinkat - SYSCALL_TABLE_ID0] = PPM_SC_UNLINKAT, [__NR_ia32_renameat - SYSCALL_TABLE_ID0] = PPM_SC_RENAMEAT, [__NR_ia32_linkat - SYSCALL_TABLE_ID0] = PPM_SC_LINKAT, [__NR_ia32_symlinkat - SYSCALL_TABLE_ID0] = PPM_SC_SYMLINKAT, [__NR_ia32_readlinkat - SYSCALL_TABLE_ID0] = PPM_SC_READLINKAT, [__NR_ia32_fchmodat - SYSCALL_TABLE_ID0] = PPM_SC_FCHMODAT, [__NR_ia32_faccessat - SYSCALL_TABLE_ID0] = PPM_SC_FACCESSAT, [__NR_ia32_pselect6 - SYSCALL_TABLE_ID0] = PPM_SC_PSELECT6, [__NR_ia32_ppoll - SYSCALL_TABLE_ID0] = PPM_SC_PPOLL, [__NR_ia32_unshare - SYSCALL_TABLE_ID0] = PPM_SC_UNSHARE, [__NR_ia32_set_robust_list - SYSCALL_TABLE_ID0] = PPM_SC_SET_ROBUST_LIST, [__NR_ia32_get_robust_list - SYSCALL_TABLE_ID0] = PPM_SC_GET_ROBUST_LIST, [__NR_ia32_splice - SYSCALL_TABLE_ID0] = PPM_SC_SPLICE, [__NR_ia32_tee - SYSCALL_TABLE_ID0] = PPM_SC_TEE, [__NR_ia32_vmsplice - SYSCALL_TABLE_ID0] = PPM_SC_VMSPLICE, #ifdef __NR_ia32_getcpu [__NR_ia32_getcpu - SYSCALL_TABLE_ID0] = PPM_SC_GETCPU, #endif [__NR_ia32_epoll_pwait - SYSCALL_TABLE_ID0] = PPM_SC_EPOLL_PWAIT, [__NR_ia32_utimensat - SYSCALL_TABLE_ID0] = PPM_SC_UTIMENSAT, [__NR_ia32_signalfd - SYSCALL_TABLE_ID0] = PPM_SC_SIGNALFD, [__NR_ia32_timerfd_create - SYSCALL_TABLE_ID0] = PPM_SC_TIMERFD_CREATE, [__NR_ia32_eventfd - SYSCALL_TABLE_ID0] = PPM_SC_EVENTFD, [__NR_ia32_timerfd_settime - SYSCALL_TABLE_ID0] = PPM_SC_TIMERFD_SETTIME, [__NR_ia32_timerfd_gettime - SYSCALL_TABLE_ID0] = PPM_SC_TIMERFD_GETTIME, [__NR_ia32_signalfd4 - SYSCALL_TABLE_ID0] = PPM_SC_SIGNALFD4, [__NR_ia32_eventfd2 - SYSCALL_TABLE_ID0] = PPM_SC_EVENTFD2, [__NR_ia32_epoll_create1 - SYSCALL_TABLE_ID0] = PPM_SC_EPOLL_CREATE1, [__NR_ia32_dup3 - SYSCALL_TABLE_ID0] = PPM_SC_DUP3, [__NR_ia32_pipe2 - SYSCALL_TABLE_ID0] = PPM_SC_PIPE2, [__NR_ia32_inotify_init1 - SYSCALL_TABLE_ID0] = PPM_SC_INOTIFY_INIT1, [__NR_ia32_preadv - SYSCALL_TABLE_ID0] = PPM_SC_PREADV, [__NR_ia32_pwritev - SYSCALL_TABLE_ID0] = PPM_SC_PWRITEV, [__NR_ia32_rt_tgsigqueueinfo - SYSCALL_TABLE_ID0] = PPM_SC_RT_TGSIGQUEUEINFO, [__NR_ia32_perf_event_open - SYSCALL_TABLE_ID0] = PPM_SC_PERF_EVENT_OPEN, #ifdef __NR_ia32_fanotify_init [__NR_ia32_fanotify_init - SYSCALL_TABLE_ID0] = PPM_SC_FANOTIFY_INIT, #endif #ifdef __NR_ia32_prlimit64 [__NR_ia32_prlimit64 - SYSCALL_TABLE_ID0] = PPM_SC_PRLIMIT64, #endif #ifdef __NR_ia32_clock_adjtime [__NR_ia32_clock_adjtime - SYSCALL_TABLE_ID0] = PPM_SC_CLOCK_ADJTIME, #endif #ifdef __NR_ia32_syncfs [__NR_ia32_syncfs - SYSCALL_TABLE_ID0] = PPM_SC_SYNCFS, #endif #ifdef __NR_ia32_setns [__NR_ia32_setns - SYSCALL_TABLE_ID0] = PPM_SC_SETNS, #endif [__NR_ia32_getdents64 - SYSCALL_TABLE_ID0] = PPM_SC_GETDENTS64, #ifndef __NR_ia32_socketcall /* * Non-multiplexed socket family */ [__NR_ia32_socket - SYSCALL_TABLE_ID0] = PPM_SC_SOCKET, [__NR_ia32_bind - SYSCALL_TABLE_ID0] = PPM_SC_BIND, [__NR_ia32_connect - SYSCALL_TABLE_ID0] = PPM_SC_CONNECT, [__NR_ia32_listen - SYSCALL_TABLE_ID0] = PPM_SC_LISTEN, [__NR_ia32_accept - SYSCALL_TABLE_ID0] = PPM_SC_ACCEPT, [__NR_ia32_getsockname - SYSCALL_TABLE_ID0] = PPM_SC_GETSOCKNAME, [__NR_ia32_getpeername - SYSCALL_TABLE_ID0] = PPM_SC_GETPEERNAME, [__NR_ia32_socketpair - SYSCALL_TABLE_ID0] = PPM_SC_SOCKETPAIR, /* [__NR_ia32_send - SYSCALL_TABLE_ID0] = PPM_SC_NR_SEND, */ [__NR_ia32_sendto - SYSCALL_TABLE_ID0] = PPM_SC_SENDTO, /* [__NR_ia32_recv - SYSCALL_TABLE_ID0] = PPM_SC_NR_RECV, */ [__NR_ia32_recvfrom - SYSCALL_TABLE_ID0] = PPM_SC_RECVFROM, [__NR_ia32_shutdown - SYSCALL_TABLE_ID0] = PPM_SC_SHUTDOWN, [__NR_ia32_setsockopt - SYSCALL_TABLE_ID0] = PPM_SC_SETSOCKOPT, [__NR_ia32_getsockopt - SYSCALL_TABLE_ID0] = PPM_SC_GETSOCKOPT, [__NR_ia32_sendmsg - SYSCALL_TABLE_ID0] = PPM_SC_SENDMSG, [__NR_ia32_recvmsg - SYSCALL_TABLE_ID0] = PPM_SC_RECVMSG, [__NR_ia32_accept4 - SYSCALL_TABLE_ID0] = PPM_SC_ACCEPT4, #else [__NR_ia32_socketcall - SYSCALL_TABLE_ID0] = PPM_SC_SOCKETCALL, #endif #ifdef __NR_ia32_sendmmsg [__NR_ia32_sendmmsg - SYSCALL_TABLE_ID0] = PPM_SC_SENDMMSG, #endif #ifdef __NR_ia32_recvmmsg [__NR_ia32_recvmmsg - SYSCALL_TABLE_ID0] = PPM_SC_RECVMMSG, #endif /* * Non-multiplexed IPC family */ #ifdef __NR_ia32_semop [__NR_ia32_semop - SYSCALL_TABLE_ID0] = PPM_SC_SEMOP, #endif #ifdef __NR_ia32_semget [__NR_ia32_semget - SYSCALL_TABLE_ID0] = PPM_SC_SEMGET, #endif #ifdef __NR_ia32_semctl [__NR_ia32_semctl - SYSCALL_TABLE_ID0] = PPM_SC_SEMCTL, #endif #ifdef __NR_ia32_semget [__NR_ia32_semget - SYSCALL_TABLE_ID0] = PPM_SC_SEMGET, #endif #ifdef __NR_ia32_msgsnd [__NR_ia32_msgsnd - SYSCALL_TABLE_ID0] = PPM_SC_MSGSND, #endif #ifdef __NR_ia32_msgrcv [__NR_ia32_msgrcv - SYSCALL_TABLE_ID0] = PPM_SC_MSGRCV, #endif #ifdef __NR_ia32_msgget [__NR_ia32_msgget - SYSCALL_TABLE_ID0] = PPM_SC_MSGGET, #endif #ifdef __NR_ia32_msgctl [__NR_ia32_msgctl - SYSCALL_TABLE_ID0] = PPM_SC_MSGCTL, #endif /* [__NR_ia32_shmatcall - SYSCALL_TABLE_ID0] = PPM_SC_NR_SHMATCALL, */ #ifdef __NR_ia32_shmdt [__NR_ia32_shmdt - SYSCALL_TABLE_ID0] = PPM_SC_SHMDT, #endif #ifdef __NR_ia32_shmget [__NR_ia32_shmget - SYSCALL_TABLE_ID0] = PPM_SC_SHMGET, #endif #ifdef __NR_ia32_shmctl [__NR_ia32_shmctl - SYSCALL_TABLE_ID0] = PPM_SC_SHMCTL, #endif /* [__NR_ia32_fcntl64 - SYSCALL_TABLE_ID0] = PPM_SC_NR_FCNTL64, */ #ifdef __NR_ia32_statfs64 [__NR_ia32_statfs64 - SYSCALL_TABLE_ID0] = PPM_SC_STATFS64, #endif #ifdef __NR_ia32_fstatfs64 [__NR_ia32_fstatfs64 - SYSCALL_TABLE_ID0] = PPM_SC_FSTATFS64, #endif #ifdef __NR_ia32_fstatat64 [__NR_ia32_fstatat64 - SYSCALL_TABLE_ID0] = PPM_SC_FSTATAT64, #endif #ifdef __NR_ia32_sendfile64 [__NR_ia32_sendfile64 - SYSCALL_TABLE_ID0] = PPM_SC_SENDFILE64, #endif #ifdef __NR_ia32_ugetrlimit [__NR_ia32_ugetrlimit - SYSCALL_TABLE_ID0] = PPM_SC_UGETRLIMIT, #endif #ifdef __NR_ia32_bdflush [__NR_ia32_bdflush - SYSCALL_TABLE_ID0] = PPM_SC_BDFLUSH, #endif #ifdef __NR_ia32_sigprocmask [__NR_ia32_sigprocmask - SYSCALL_TABLE_ID0] = PPM_SC_SIGPROCMASK, #endif #ifdef __NR_ia32_ipc [__NR_ia32_ipc - SYSCALL_TABLE_ID0] = PPM_SC_IPC, #endif #ifdef __NR_ia32_stat64 [__NR_ia32_stat64 - SYSCALL_TABLE_ID0] = PPM_SC_STAT64, #endif #ifdef __NR_ia32_lstat64 [__NR_ia32_lstat64 - SYSCALL_TABLE_ID0] = PPM_SC_LSTAT64, #endif #ifdef __NR_ia32_fstat64 [__NR_ia32_fstat64 - SYSCALL_TABLE_ID0] = PPM_SC_FSTAT64, #endif #ifdef __NR_ia32_fcntl64 [__NR_ia32_fcntl64 - SYSCALL_TABLE_ID0] = PPM_SC_FCNTL64, #endif #ifdef __NR_ia32_mmap2 [__NR_ia32_mmap2 - SYSCALL_TABLE_ID0] = PPM_SC_MMAP2, #endif #ifdef __NR_ia32__newselect [__NR_ia32__newselect - SYSCALL_TABLE_ID0] = PPM_SC__NEWSELECT, #endif #ifdef __NR_ia32_sgetmask [__NR_ia32_sgetmask - SYSCALL_TABLE_ID0] = PPM_SC_SGETMASK, #endif #ifdef __NR_ia32_ssetmask [__NR_ia32_ssetmask - SYSCALL_TABLE_ID0] = PPM_SC_SSETMASK, #endif /* [__NR_ia32_setreuid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETREUID16, */ /* [__NR_ia32_setregid16 - SYSCALL_TABLE_ID0] = PPM_SC_NR_SETREGID16, */ #ifdef __NR_ia32_sigpending [__NR_ia32_sigpending - SYSCALL_TABLE_ID0] = PPM_SC_SIGPENDING, #endif #ifdef __NR_ia32_olduname [__NR_ia32_olduname - SYSCALL_TABLE_ID0] = PPM_SC_OLDUNAME, #endif #ifdef __NR_ia32_umount [__NR_ia32_umount - SYSCALL_TABLE_ID0] = PPM_SC_UMOUNT, #endif #ifdef __NR_ia32_signal [__NR_ia32_signal - SYSCALL_TABLE_ID0] = PPM_SC_SIGNAL, #endif #ifdef __NR_ia32_nice [__NR_ia32_nice - SYSCALL_TABLE_ID0] = PPM_SC_NICE, #endif #ifdef __NR_ia32_stime [__NR_ia32_stime - SYSCALL_TABLE_ID0] = PPM_SC_STIME, #endif #ifdef __NR_ia32__llseek [__NR_ia32__llseek - SYSCALL_TABLE_ID0] = PPM_SC__LLSEEK, #endif #ifdef __NR_ia32_waitpid [__NR_ia32_waitpid - SYSCALL_TABLE_ID0] = PPM_SC_WAITPID, #endif #ifdef __NR_ia32_pread64 [__NR_ia32_pread64 - SYSCALL_TABLE_ID0] = PPM_SC_PREAD64, #endif #ifdef __NR_ia32_pwrite64 [__NR_ia32_pwrite64 - SYSCALL_TABLE_ID0] = PPM_SC_PWRITE64, #endif #ifdef __NR_ia32_shmat [__NR_ia32_shmat - SYSCALL_TABLE_ID0] = PPM_SC_SHMAT, #endif #ifdef __NR_ia32_rt_sigreturn [__NR_ia32_rt_sigreturn - SYSCALL_TABLE_ID0] = PPM_SC_SIGRETURN, #endif #ifdef __NR_ia32_fallocate [__NR_ia32_fallocate - SYSCALL_TABLE_ID0] = PPM_SC_FALLOCATE, #endif #ifdef __NR_ia32_newfstatat [__NR_ia32_newfstatat - SYSCALL_TABLE_ID0] = PPM_SC_NEWFSSTAT, #endif #ifdef __NR_ia32_process_vm_readv [__NR_ia32_process_vm_readv - SYSCALL_TABLE_ID0] = PPM_SC_PROCESS_VM_READV, #endif #ifdef __NR_ia32_process_vm_writev [__NR_ia32_process_vm_writev - SYSCALL_TABLE_ID0] = PPM_SC_PROCESS_VM_WRITEV, #endif #ifdef __NR_ia32_fork [__NR_ia32_fork - SYSCALL_TABLE_ID0] = PPM_SC_FORK, #endif #ifdef __NR_ia32_vfork [__NR_ia32_vfork - SYSCALL_TABLE_ID0] = PPM_SC_VFORK, #endif #ifdef __NR_ia32_quotactl [__NR_ia32_quotactl - SYSCALL_TABLE_ID0] = PPM_SC_QUOTACTL, #endif #ifdef __NR_ia32_setresuid [__NR_ia32_setresuid - SYSCALL_TABLE_ID0] = PPM_SC_SETRESUID, #endif #ifdef __NR_ia32_setresuid32 [__NR_ia32_setresuid32 - SYSCALL_TABLE_ID0] = PPM_SC_SETRESUID, #endif #ifdef __NR_ia32_setresgid [__NR_ia32_setresgid - SYSCALL_TABLE_ID0] = PPM_SC_SETRESGID, #endif #ifdef __NR_ia32_setresgid32 [__NR_ia32_setresgid32 - SYSCALL_TABLE_ID0] = PPM_SC_SETRESGID, #endif #ifdef __NR_ia32_setuid [__NR_ia32_setuid - SYSCALL_TABLE_ID0] = PPM_SC_SETUID, #endif #ifdef __NR_ia32_setuid32 [__NR_ia32_setuid32 - SYSCALL_TABLE_ID0] = PPM_SC_SETUID32, #endif #ifdef __NR_ia32_setgid [__NR_ia32_setgid - SYSCALL_TABLE_ID0] = PPM_SC_SETGID, #endif #ifdef __NR_ia32_setgid32 [__NR_ia32_setgid32 - SYSCALL_TABLE_ID0] = PPM_SC_SETGID32, #endif #ifdef __NR_ia32_getuid [__NR_ia32_getuid - SYSCALL_TABLE_ID0] = PPM_SC_GETUID, #endif #ifdef __NR_ia32_getuid32 [__NR_ia32_getuid32 - SYSCALL_TABLE_ID0] = PPM_SC_GETUID32, #endif #ifdef __NR_ia32_geteuid [__NR_ia32_geteuid - SYSCALL_TABLE_ID0] = PPM_SC_GETEUID, #endif #ifdef __NR_ia32_geteuid32 [__NR_ia32_geteuid32 - SYSCALL_TABLE_ID0] = PPM_SC_GETEUID, #endif #ifdef __NR_ia32_getgid [__NR_ia32_getgid - SYSCALL_TABLE_ID0] = PPM_SC_GETGID, #endif #ifdef __NR_ia32_getgid32 [__NR_ia32_getgid32 - SYSCALL_TABLE_ID0] = PPM_SC_GETGID, #endif #ifdef __NR_ia32_getegid [__NR_ia32_getegid - SYSCALL_TABLE_ID0] = PPM_SC_GETEGID, #endif #ifdef __NR_ia32_getegid32 [__NR_ia32_getegid32 - SYSCALL_TABLE_ID0] = PPM_SC_GETEGID, #endif #ifdef __NR_ia32_getresuid [__NR_ia32_getresuid - SYSCALL_TABLE_ID0] = PPM_SC_GETRESUID, #endif #ifdef __NR_ia32_getresuid32 [__NR_ia32_getresuid32 - SYSCALL_TABLE_ID0] = PPM_SC_GETRESUID32, #endif #ifdef __NR_ia32_getresgid [__NR_ia32_getresgid - SYSCALL_TABLE_ID0] = PPM_SC_GETRESGID, #endif #ifdef __NR_ia32_getresgid32 [__NR_ia32_getresgid32 - SYSCALL_TABLE_ID0] = PPM_SC_GETRESGID32, #endif #ifdef __NR_ia32_finit_module [__NR_ia32_finit_module - SYSCALL_TABLE_ID0] = PPM_SC_FINIT_MODULE, #endif #ifdef __NR_ia32_bpf [__NR_ia32_bpf - SYSCALL_TABLE_ID0] = PPM_SC_BPF, #endif #ifdef __NR_ia32_seccomp [__NR_ia32_seccomp - SYSCALL_TABLE_ID0] = PPM_SC_SECCOMP, #endif #ifdef __NR_ia32_sigaltstack [__NR_ia32_sigaltstack - SYSCALL_TABLE_ID0] = PPM_SC_SIGALTSTACK, #endif #ifdef __NR_ia32_getrandom [__NR_ia32_getrandom - SYSCALL_TABLE_ID0] = PPM_SC_GETRANDOM, #endif #ifdef __NR_ia32_fadvise64 [__NR_ia32_fadvise64 - SYSCALL_TABLE_ID0] = PPM_SC_FADVISE64, #endif }; #endif /* CONFIG_IA32_EMULATION */ sysdig-0.26.4/scripts/000077500000000000000000000000001352731327100146005ustar00rootroot00000000000000sysdig-0.26.4/scripts/CMakeLists.txt000066400000000000000000000020051352731327100173350ustar00rootroot00000000000000# # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # configure_file(debian/postinst.in debian/postinst) configure_file(debian/prerm.in debian/prerm) install(FILES completions/bash/sysdig DESTINATION "${DIR_ETC}/bash_completion.d") install(FILES completions/zsh/_sysdig DESTINATION share/zsh/vendor-completions) install(FILES completions/zsh/_sysdig DESTINATION share/zsh/site-functions) install(PROGRAMS sysdig-probe-loader DESTINATION bin) sysdig-0.26.4/scripts/Dockerfile.ol6000066400000000000000000000007121352731327100172710ustar00rootroot00000000000000FROM oraclelinux:6 RUN yum -y install \ wget \ git \ gcc \ gcc-c++ \ autoconf \ make \ cmake \ libdtrace-ctf \ elfutils-devel \ python-lxml && yum clean all WORKDIR /build # Use the same directory structure as the jenkins worker RUN mkdir -p sysdig/scripts ADD oracle-kernel-crawler.py sysdig/scripts/ ADD build-probe-binaries sysdig/scripts/ WORKDIR probe ENTRYPOINT [ "../sysdig/scripts/build-probe-binaries" ] sysdig-0.26.4/scripts/Dockerfile.ol7000066400000000000000000000007341352731327100172760ustar00rootroot00000000000000FROM oraclelinux:7 RUN yum -y install \ wget \ git \ gcc \ gcc-c++ \ autoconf \ make \ cmake \ libdtrace-ctf \ elfutils-libelf-devel \ file \ python-lxml && yum clean all WORKDIR /build # Use the same directory structure as the jenkins worker RUN mkdir -p sysdig/scripts ADD oracle-kernel-crawler.py sysdig/scripts/ ADD build-probe-binaries sysdig/scripts/ WORKDIR probe ENTRYPOINT [ "../sysdig/scripts/build-probe-binaries" ] sysdig-0.26.4/scripts/boot2docker-kernel-crawler.py000077500000000000000000000036631352731327100223150ustar00rootroot00000000000000#!/usr/bin/python # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # # Author: Ethan Sutin from lxml import etree import urllib2,re from distutils.version import LooseVersion response = urllib2.urlopen('https://github.com/boot2docker/boot2docker/tags.atom') ns_map = {'ns': 'http://www.w3.org/2005/Atom'} data = etree.fromstring(response.read()) release_nodes = data.xpath('//ns:feed/ns:entry/ns:title', namespaces=ns_map) for release in release_nodes: version = release.text if ':' in version: version = version[:version.index(':')] # tracepoints only enabled >= 1.7.0 if LooseVersion(version[1:]) >= LooseVersion('1.7'): dockerFile = urllib2.urlopen('https://raw.githubusercontent.com/boot2docker/boot2docker/%s/Dockerfile' % (version)).read() for line in dockerFile.split('\n'): if re.search('ENV KERNEL_VERSION', line): kernel_version = line.split()[-1] if re.search('ENV AUFS_BRANCH', line): aufs_branch = line.split()[-1] if re.search('ENV AUFS_COMMIT', line): aufs_commit = line.split()[-1] print 'boot2docker-%s %s-boot2docker https://www.kernel.org/pub/linux/kernel/v4.x/linux-%s.tar.xz https://raw.githubusercontent.com/boot2docker/boot2docker/%s/kernel_config https://github.com/sfjro/aufs4-standalone %s %s' % \ (version[1:],kernel_version,kernel_version,version,aufs_branch,aufs_commit) sysdig-0.26.4/scripts/build-probe-binaries000077500000000000000000000530031352731327100205250ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # # # This script builds a precompiled version of sysdig-probe for a bunch of kernels # The precompiled binary is then obtained at runtime by sysdig-probe-loader # Ideally, the community should expand this stuff with better support # set -euo pipefail PROBE_NAME=$1 PROBE_VERSION=$2 REPOSITORY_NAME=$3 KERNEL_TYPE= INTERNAL_ARTIFACTORY_API_KEY= BASEDIR=$(pwd) ARCH=$(uname -m) URL_TIMEOUT=300 RETRY=10 if [ $# -ge 4 ]; then KERNEL_TYPE=$4 echo KERNEL TYPE is $KERNEL_TYPE if [ "RHEL" = "$KERNEL_TYPE" ]; then INTERNAL_ARTIFACTORY_API_KEY=$5 INTERNAL_ARTIFACTORY_SERVER=$6 fi fi if [ ! -d $BASEDIR/output ]; then mkdir $BASEDIR/output fi PROBE_DEVICE_NAME=$(echo $PROBE_NAME | cut -f1 -d-) if [ $PROBE_NAME = "sysdigcloud-probe" ]; then SYSDIG_TAG="agent/${PROBE_VERSION}" elif [ $PROBE_NAME = "sysdig-probe" ]; then SYSDIG_TAG="${PROBE_VERSION}" else SYSDIG_TAG="${PROBE_DEVICE_NAME}/${PROBE_VERSION}" fi # The previously released versions of sysdig, falco, and the agent # don't have one of the necessary changes that this script requires # (namely, the ability to run cmake from the sysdig directory but # specify PROBE_NAME, PROBE_VERSION, and PROBE_DEVICE_NAME). Rather # than change what was in the tag we made modified tags x.y.z-cfgprobe # that include the sysdig version x.y.z but also one additional commit # that allows making the probe name/version/etc configurable. if [ $SYSDIG_TAG = "0.24.1" ] || [ $SYSDIG_TAG = "agent/0.85.1" ] || [ $SYSDIG_TAG = "falco/0.12.1" ]; then SYSDIG_TAG="${SYSDIG_TAG}-cfgprobe" fi function checkout_sysdig { rm -rf sysdig || true git clone git@github.com:draios/sysdig.git cd sysdig git checkout $SYSDIG_TAG cd .. } function clean_sysdig { cd sysdig # Remove everything other than the files actually belonging to # the repo. git clean -d -f -x # Reset the state of the files belonging to the repo to the # state associated with the tag. git reset --hard cd .. } function build_probe { # Skip Kernel 4.15.0-29 because probe does not build against it if [ $KERNEL_RELEASE-$HASH = "4.15.0-29-generic-ea0aa038a6b9bdc4bb42152682bba6ce" ] then echo "Temporarily skipping " $PROBE_NAME-$PROBE_VERSION-$ARCH-$KERNEL_RELEASE-$HASH.ko return fi if [ ! -f $BASEDIR/output/$PROBE_NAME-$PROBE_VERSION-$ARCH-$KERNEL_RELEASE-$HASH.ko ] || [ ! -f $BASEDIR/output/$PROBE_NAME-$PROBE_VERSION-$ARCH-$KERNEL_RELEASE-$HASH_ORIG.ko ]; then if [[ -f "${KERNELDIR}/scripts/gcc-plugins/stackleak_plugin.so" ]]; then echo "Rebuilding gcc plugins for ${KERNELDIR}" (cd "${KERNELDIR}" && make gcc-plugins) fi echo Building $PROBE_NAME-$PROBE_VERSION-$ARCH-$KERNEL_RELEASE-$HASH.ko [${FUNCNAME[1]}] clean_sysdig cd sysdig/ mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release -DPROBE_NAME=$PROBE_NAME -DPROBE_VERSION=$PROBE_VERSION -DPROBE_DEVICE_NAME=$PROBE_DEVICE_NAME .. make driver strip -g driver/$PROBE_NAME.ko KO_VERSION=$(/sbin/modinfo driver/$PROBE_NAME.ko | grep vermagic | tr -s " " | cut -d " " -f 2) if [ "$KO_VERSION" != "$KERNEL_RELEASE" ]; then echo "Corrupted probe, KO_VERSION " $KO_VERSION ", KERNEL_RELEASE " $KERNEL_RELEASE exit 1 fi cp driver/$PROBE_NAME.ko $BASEDIR/output/$PROBE_NAME-$PROBE_VERSION-$ARCH-$KERNEL_RELEASE-$HASH.ko cp driver/$PROBE_NAME.ko $BASEDIR/output/$PROBE_NAME-$PROBE_VERSION-$ARCH-$KERNEL_RELEASE-$HASH_ORIG.ko else echo Skipping $PROBE_NAME-$PROBE_VERSION-$ARCH-$KERNEL_RELEASE-$HASH.ko \(already built\) fi cd $BASEDIR } function coreos_build_old { VERSION_URL=$1 VERSION_NUMBER=$2 COREOS_DIR="coreos-"$VERSION_NUMBER if [ ! -d $COREOS_DIR ]; then mkdir $COREOS_DIR fi cd $COREOS_DIR if [ ! -f config_orig ]; then wget -nv --timeout=${URL_TIMEOUT} --tries=${RETRY} ${VERSION_URL}coreos_developer_container.bin.bz2 bunzip2 coreos_developer_container.bin.bz2 sudo kpartx -asv coreos_developer_container.bin LOOPDEV=$(sudo kpartx -asv coreos_developer_container.bin | cut -d\ -f 3) sudo mkdir /tmp/loop || true sudo mount /dev/mapper/$LOOPDEV /tmp/loop cp /tmp/loop/usr/boot/config-* . sudo umount /tmp/loop sudo kpartx -dv coreos_developer_container.bin rm -rf coreos_developer_container.bin cp config-* config_orig fi KERNEL_RELEASE=$(ls config-* | sed s/config-//) VANILLA=$(echo $KERNEL_RELEASE | sed s/[-+].*// | sed s/\.0$//) MAJOR=$(echo $KERNEL_RELEASE | head -c1) EXTRAVERSION=$(echo $KERNEL_RELEASE | sed s/[^-+]*//) TGZ_NAME=linux-${VANILLA}.tar.xz DIR_NAME=linux-${VANILLA} KERNEL_URL=https://www.kernel.org/pub/linux/kernel/v${MAJOR}.x/$TGZ_NAME if [ ! -f $TGZ_NAME ]; then wget -nv --timeout=${URL_TIMEOUT} --tries=${RETRY} $KERNEL_URL fi if [ ! -d $DIR_NAME ]; then tar xf $TGZ_NAME cd $DIR_NAME make distclean sed -i "s/^EXTRAVERSION.*/EXTRAVERSION = $EXTRAVERSION/" Makefile cp ../config_orig .config make modules_prepare mv .config ../config cd .. fi HASH=$(md5sum config | cut -d' ' -f1) HASH_ORIG=$(md5sum config_orig | cut -d' ' -f1) cd $BASEDIR export KERNELDIR=$BASEDIR/$COREOS_DIR/$DIR_NAME build_probe } function coreos_build_new { VERSION_URL=$1 VERSION_NUMBER=$2 COREOS_DIR="coreos-"$VERSION_NUMBER if [ ! -d $COREOS_DIR ]; then mkdir $COREOS_DIR fi cd $COREOS_DIR if [ ! -f coreos_developer_container.bin ]; then wget -nv --timeout=${URL_TIMEOUT} --tries=${RETRY} ${VERSION_URL}coreos_developer_container.bin.bz2 bunzip2 coreos_developer_container.bin.bz2 fi if [ ! -f config_orig ]; then # mount developer container is a very stateful part of this script # the section between mount/unmounting should be kept very small # otherwise if something fails there are many inconsistencies that can happen sudo kpartx -asv coreos_developer_container.bin LOOPDEV=$(sudo kpartx -asv coreos_developer_container.bin | cut -d\ -f 3) sudo mkdir /tmp/loop || true sudo mount /dev/mapper/$LOOPDEV /tmp/loop # Copy kernel headers cp -r /tmp/loop/lib/modules . # Copy kernel config rm config* || true cp /tmp/loop/usr/boot/config-* . cp config-* config_orig cp config_orig config # umount and remove the developer container sudo umount /tmp/loop sudo kpartx -dv coreos_developer_container.bin fi # https://groups.google.com/forum/#!topic/coreos-dev/Z8Q7sIy6YwE sed -i 's/CONFIG_INITRAMFS_SOURCE=""/CONFIG_INITRAMFS_SOURCE="bootengine.cpio"\nCONFIG_INITRAMFS_ROOT_UID=0\nCONFIG_INITRAMFS_ROOT_GID=0/' config sed -i 'N;s/\(CONFIG_RD_LZ4=.\)\n\(CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=\)/\1\nCONFIG_INITRAMFS_COMPRESSION=".gz"\n\2/' config KERNEL_RELEASE=$(ls config-* | sed s/config-//) HASH_ORIG=$(md5sum config_orig | cut -d' ' -f1) HASH=$(md5sum config | cut -d' ' -f1) export KERNELDIR=$PWD/modules/$KERNEL_RELEASE/build cd $BASEDIR build_probe return } function boot2docker_build { CONFIGURATION_NAME=$1 KERNEL_RELEASE=$2 KERNEL_URL=$3 KERNEL_CONFIG=$4 AUFS_REPO=$5 AUFS_BRANCH=$6 AUFS_COMMIT=$7 TGZ_NAME=$(echo $KERNEL_URL | awk -F"/" '{print $NF }') DIR_NAME=$(echo $TGZ_NAME | sed 's/.tar.xz//') if [ ! -d $CONFIGURATION_NAME ]; then mkdir $CONFIGURATION_NAME fi cd $CONFIGURATION_NAME if [ ! -f $TGZ_NAME ]; then echo Downloading $TGZ_NAME [Boot2Docker] wget -nv --timeout=${URL_TIMEOUT} --tries=${RETRY} $KERNEL_URL fi if [ ! -d $DIR_NAME ]; then tar xf $TGZ_NAME cd $DIR_NAME make distclean git clone -b "$AUFS_BRANCH" "$AUFS_REPO" aufs-standalone cd aufs-standalone git checkout -q "$AUFS_COMMIT" cd .. cp -r aufs-standalone/Documentation . cp -r aufs-standalone/fs . cp -r aufs-standalone/include/uapi/linux/aufs_type.h include/uapi/linux/ set -e && for patch in \ aufs-standalone/aufs*-kbuild.patch \ aufs-standalone/aufs*-base.patch \ aufs-standalone/aufs*-mmap.patch \ aufs-standalone/aufs*-standalone.patch \ aufs-standalone/aufs*-loopback.patch \ ; do \ patch -p1 < "$patch"; \ done wget -nv --timeout=${URL_TIMEOUT} --tries=${RETRY} -O .config $KERNEL_CONFIG cp .config ../config-orig make olddefconfig make modules_prepare mv .config ../config cd .. fi HASH=$(md5sum config | cut -d' ' -f1) HASH_ORIG=$(md5sum config-orig | cut -d' ' -f1) cd $BASEDIR export KERNELDIR=$BASEDIR/$CONFIGURATION_NAME/$DIR_NAME build_probe } function ubuntu_build { URL=$1 DEB=$(echo $URL | grep -o '[^/]*$') KERNEL_RELEASE_FULL=$(echo $DEB | grep -E -o "[0-9]{1}\.[0-9]+\.[0-9]+-[0-9]+\.[0-9]+") # ex. 3.13.0-24.47 KERNEL_RELEASE=$(echo $KERNEL_RELEASE_FULL | grep -E -o "[0-9]{1}\.[0-9]+\.[0-9]+-[0-9]+") # ex. 3.13.0-24 KERNEL_UPDATE=$(echo $KERNEL_RELEASE_FULL | grep -E -o "[0-9]+$") # ex. 47 if [ ! -d $KERNEL_RELEASE ]; then mkdir $KERNEL_RELEASE fi cd $KERNEL_RELEASE if [ ! -d $KERNEL_UPDATE ]; then mkdir $KERNEL_UPDATE fi cd $KERNEL_UPDATE if [ ! -f $DEB ]; then echo Downloading $DEB [Ubuntu] wget -nv --timeout=${URL_TIMEOUT} --tries=${RETRY} $URL dpkg -x $DEB ./ fi NUM_DEB=$(ls linux-*.deb -1 | wc -l) if [ $NUM_DEB -eq 4 ]; then local KERNEL_FOLDER=$KERNEL_RELEASE KERNEL_RELEASE=$(ls -1 linux-image-* | grep -E -o "[0-9]{1}\.[0-9]+\.[0-9]+-[0-9]+-[a-z]+") HASH=$(md5sum boot/config-$KERNEL_RELEASE | cut -d' ' -f1) HASH_ORIG=$HASH cd $BASEDIR export KERNELDIR=$BASEDIR/$KERNEL_FOLDER/$KERNEL_UPDATE/usr/src/linux-headers-$KERNEL_RELEASE build_probe fi cd $BASEDIR } function rhel_build { # # The function just requires the rpm url # # Get all the parameters needed URL=$1 # Optionally take an artifactory api key. ARTIFACTORY_API_KEY="${2:-""}" RPM=$(echo $URL | grep -o '[^/]*$') KERNEL_RELEASE=$(echo $RPM | awk 'match($0, /[^kernel\-(uek\-)?(core\-|devel\-)?].*[^(\.rpm)]/){ print substr($0, RSTART, RLENGTH) }') if [ ! -d $KERNEL_RELEASE ]; then mkdir $KERNEL_RELEASE fi cd $KERNEL_RELEASE if [ ! -f $RPM ]; then echo Downloading $RPM [RHEL and CentOS] if [ -z "$ARTIFACTORY_API_KEY" ]; then wget -nv --timeout=${URL_TIMEOUT} --tries=${RETRY} $URL else curl -H "X-JFrog-Art-Api:$ARTIFACTORY_API_KEY" -O $URL fi rpm2cpio $RPM | cpio -idm fi NUM_RPM=$(ls kernel-*.rpm -1 | wc -l) # We need two rpms (devel and core) to get what we need if [ $NUM_RPM -eq 2 ]; then #echo Building $KERNEL_RELEASE if [ -f boot/config-$KERNEL_RELEASE ]; then HASH=$(md5sum boot/config-$KERNEL_RELEASE | cut -d' ' -f1) else HASH=$(md5sum lib/modules/$KERNEL_RELEASE/config | cut -d' ' -f1) fi HASH_ORIG=$HASH cd $BASEDIR export KERNELDIR=$BASEDIR/$KERNEL_RELEASE/usr/src/kernels/$KERNEL_RELEASE build_probe fi cd $BASEDIR } function debian_build { URL=${1} DEB=$(echo ${URL} | grep -o '[^/]*$') if [[ ${DEB} == *"kbuild"* ]]; then if [[ ! -d ${BASEDIR}/common-dependencies/debian/kbuild/ ]]; then mkdir -p ${BASEDIR}/common-dependencies/debian/kbuild fi if [ ! -f ${BASEDIR}/common-dependencies/debian/kbuild/${DEB} ]; then echo Downloading ${DEB} [Debian] wget -nv --timeout=${URL_TIMEOUT} --tries=${RETRY} -P ${BASEDIR}/common-dependencies/debian/kbuild ${URL} fi return else KERNEL_RELEASE=$(echo ${DEB} | grep -E -o "[0-9]{1}\.[0-9]+\.[0-9]+(-[0-9]+)?"| head -1) KERNEL_MAJOR=$(echo ${KERNEL_RELEASE} | grep -E -o "[0-9]{1}\.[0-9]+") PACKAGE=$(echo ${DEB} | grep -E -o "(common_[0-9]{1}\.[0-9]+.*amd64|amd64_[0-9]{1}\.[0-9]+.*amd64)" | sed -E 's/(common_|amd64_|_amd64)//g') if [[ ! -d ${KERNEL_RELEASE} ]]; then mkdir ${KERNEL_RELEASE} fi cd ${KERNEL_RELEASE} if [ ! -d ${PACKAGE} ]; then mkdir ${PACKAGE} fi cd ${PACKAGE} if [ ! -f ${DEB} ]; then echo Downloading ${DEB} [Debian] wget -nv --timeout=${URL_TIMEOUT} --tries=${RETRY} ${URL} dpkg -x ${DEB} ./ fi fi NUM_DEB=$(ls linux-*.deb -1| grep -v kbuild | wc -l) if [[ ${NUM_DEB} -eq 3 ]]; then set +e KBUILD_PACKAGE=$(ls -t ${BASEDIR}/common-dependencies/debian/kbuild | grep "kbuild\-${KERNEL_MAJOR}" | head -1) set -e if [[ ! -z ${KBUILD_PACKAGE} ]]; then cp ${BASEDIR}/common-dependencies/debian/kbuild/${KBUILD_PACKAGE} . dpkg -x ${KBUILD_PACKAGE} ./ local KERNEL_FOLDER=${KERNEL_RELEASE} KERNEL_RELEASE=$(ls boot/config-* | sed 's|boot/config-||') HASH=$(md5sum boot/config-${KERNEL_RELEASE} | cut -d' ' -f1) HASH_ORIG=${HASH} export KERNELDIR=${BASEDIR}/${KERNEL_FOLDER}/${PACKAGE}/usr/src/linux-headers-${KERNEL_RELEASE} #fix symbolic links unlink ${BASEDIR}/${KERNEL_FOLDER}/${PACKAGE}/lib/modules/${KERNEL_RELEASE}/build ln -s ${BASEDIR}/${KERNEL_FOLDER}/${PACKAGE}/usr/src/linux-headers-${KERNEL_RELEASE} ${BASEDIR}/${KERNEL_FOLDER}/${PACKAGE}/lib/modules/${KERNEL_RELEASE}/build common_folder=$(ls ${BASEDIR}/${KERNEL_FOLDER}/${PACKAGE}/usr/src/ | egrep '*common') unlink ${BASEDIR}/${KERNEL_FOLDER}/${PACKAGE}/lib/modules/${KERNEL_RELEASE}/source ln -s ${BASEDIR}/${KERNEL_FOLDER}/${PACKAGE}/usr/src/${common_folder} ${BASEDIR}/${KERNEL_FOLDER}/${PACKAGE}/lib/modules/${KERNEL_RELEASE}/source #hack Makefile sed -i '0,/MAKEARGS.*$/s||MAKEARGS := -C '"${BASEDIR}/${KERNEL_FOLDER}/${PACKAGE}/usr/src/${common_folder}"' O='"${BASEDIR}/${KERNEL_FOLDER}/${PACKAGE}/usr/src/linux-headers-${KERNEL_RELEASE}"'|' ${BASEDIR}/${KERNEL_FOLDER}/${PACKAGE}/usr/src/linux-headers-${KERNEL_RELEASE}/Makefile sed -i 's/@://' ${BASEDIR}/${KERNEL_FOLDER}/${PACKAGE}/usr/src/linux-headers-${KERNEL_RELEASE}/Makefile sed -i 's|$(cmd) %.*$|$(cmd) : all|' ${BASEDIR}/${KERNEL_FOLDER}/${PACKAGE}/usr/src/linux-headers-${KERNEL_RELEASE}/Makefile cd ${BASEDIR} build_probe fi fi cd ${BASEDIR} } function minikube_build { VERSION=$1 MINIKUBE_BASEDIR=${BASEDIR}/minikube if [ ! -d ${MINIKUBE_BASEDIR} ]; then mkdir -p ${MINIKUBE_BASEDIR} fi MINIKUBE_DIR=${MINIKUBE_BASEDIR}/minikube-$(echo ${VERSION} | sed 's/^v//') if [ ! -d ${MINIKUBE_DIR} ]; then wget -nv -P ${MINIKUBE_BASEDIR} --timeout=${URL_TIMEOUT} --tries=${RETRY} https://github.com/kubernetes/minikube/archive/${VERSION}.tar.gz tar -C ${MINIKUBE_BASEDIR} -zxf ${MINIKUBE_BASEDIR}/${VERSION}.tar.gz rm -f ${MINIKUBE_BASEDIR}/${VERSION}.tar.gz fi MINIKUBE_KERNEL_VERSION=$(cat ${MINIKUBE_DIR}/deploy/iso/minikube-iso/configs/minikube_defconfig | grep BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE | cut -d= -f 2 | sed 's/"//g') TGZ_NAME=linux-${MINIKUBE_KERNEL_VERSION}.tar.xz MAJOR=$(echo $MINIKUBE_KERNEL_VERSION | cut -d. -f1) MINOR=$(echo $MINIKUBE_KERNEL_VERSION | cut -d. -f2) KERNEL_CONFIG=${MINIKUBE_DIR}/deploy/iso/minikube-iso/board/coreos/minikube/linux_defconfig # The filename used to contain the kernel version if [ ! -e ${KERNEL_CONFIG} ]; then KERNEL_CONFIG=${MINIKUBE_DIR}/deploy/iso/minikube-iso/board/coreos/minikube/linux-${MAJOR}.${MINOR}_defconfig fi if [ ! -e ${KERNEL_CONFIG} ]; then echo "****ERROR: Could not find kernel config for minikube version ${VERSION}. Expected at ${KERNEL_CONFIG}" exit 1 fi export KERNELDIR=${MINIKUBE_BASEDIR}/linux-${MINIKUBE_KERNEL_VERSION} if [ ! -d ${KERNELDIR} ]; then wget -nv -P ${MINIKUBE_BASEDIR} --timeout=${URL_TIMEOUT} --tries=${RETRY} https://www.kernel.org/pub/linux/kernel/v${MAJOR}.x/${TGZ_NAME} tar -C ${MINIKUBE_BASEDIR} -xf ${MINIKUBE_BASEDIR}/${TGZ_NAME} rm -f ${MINIKUBE_BASEDIR}/${TGZ_NAME} fi pushd ${KERNELDIR} make distclean cp ${KERNEL_CONFIG} .config # Manually setting ARCH to x86_64 mirrors what buildroot does. ARCH=x86_64 make olddefconfig make modules_prepare popd HASH=$(md5sum ${KERNELDIR}/.config | cut -d' ' -f1) HASH_ORIG=${HASH} KERNEL_RELEASE=$(cat ${KERNELDIR}/include/config/kernel.release) cd $BASEDIR build_probe } if [ -z "$KERNEL_TYPE" ]; then # # Ubuntu build # checkout_sysdig echo Building Ubuntu DIR=$(dirname $(readlink -f $0)) URLS="$($DIR/kernel-crawler.py Ubuntu)" for URL in $URLS do ubuntu_build $URL done # # RHEL build through CentOS # echo Building CentOS DIR=$(dirname $(readlink -f $0)) URLS="$($DIR/kernel-crawler.py CentOS)" for URL in $URLS do rhel_build $URL done # # Fedora build # echo Building Fedora DIR=$(dirname $(readlink -f $0)) URLS="$($DIR/kernel-crawler.py Fedora)" for URL in $URLS do rhel_build $URL done # # Fedora Atomic build # echo Building Fedora Atomic DIR=$(dirname $(readlink -f $0)) URLS="$($DIR/kernel-crawler.py Fedora-Atomic)" for URL in $URLS do rhel_build $URL done # # CoreOS build # echo Building CoreOS DIR=$(dirname $(readlink -f $0)) URLS="$($DIR/kernel-crawler.py CoreOS)" for URL in $URLS do set +e eval $(curl -s ${URL}version.txt) if [ ${?} -ne 0 ]; then echo "### Error fetching ${URL}version.txt ###" continue fi set -e if [ $COREOS_BUILD -gt 890 ]; then coreos_build_new $URL $COREOS_VERSION else coreos_build_old $URL $COREOS_VERSION fi done # # boot2docker is officially in maintenance mode # Disabled boot2docker builds as they were failing because of # upstream changes to the Dockerfile. # # boot2docker build # # echo Building boot2docker # DIR=$(dirname $(readlink -f $0)) # $DIR/boot2docker-kernel-crawler.py | \ # while read KERNEL_ARGS # do # boot2docker_build $KERNEL_ARGS # done # # Debian build # echo Building Debian DIR=$(dirname $(readlink -f $0)) URLS="$($DIR/kernel-crawler.py Debian)" for URL in $URLS do debian_build $URL done # XXX agent/434 - We need to force a build for certain kernel versions # because they are still in use by some GCE customers but the headers # are no longer available from the mirror. We pass the URL but nothing # needs to be downloaded because we already have it cached on the builder. # # These are no longer available, so disabling for now. #debian_build https://mirrors.kernel.org/debian/pool/main/l/linux/linux-headers-3.16.0-4-amd64_3.16.36-1+deb8u2_amd64.deb #debian_build https://mirrors.kernel.org/debian/pool/main/l/linux/linux-headers-3.16.0-4-amd64_3.16.39-1+deb8u2_amd64.deb # # Oracle RHCK build # echo Building Oracle RHCK DIR=$(dirname $(readlink -f $0)) URLS="$($DIR/oracle-kernel-crawler.py Oracle-RHCK)" for URL in $URLS do rhel_build $URL done # # The UEK builds needs to happen in a UEK environment, so we launch # a container running OL and do the builds there. This container # doesn't have git credentials, so it relies on previous builds # to do the sysdig/falco/agent git clone/pulls. # # # Oracle Linux 6 UEK build # echo "Creating Oracle Linux 6 UEK builder" docker ps -q -f 'name=ol6-build' | xargs --no-run-if-empty docker rm docker build -t ol6-builder:latest -f ../sysdig/scripts/Dockerfile.ol6 --pull ../sysdig/scripts docker images -q -f 'dangling=true' | xargs --no-run-if-empty docker rmi docker run -i --rm --name ol6-build -v $BASEDIR:/build/probe ol6-builder $PROBE_NAME $PROBE_VERSION $REPOSITORY_NAME OL6-UEK # # Oracle Linux 7 UEK build # echo "Creating Oracle Linux 7 UEK builder" docker ps -q -f 'name=ol7-build' | xargs --no-run-if-empty docker rm docker build -t ol7-builder:latest -f ../sysdig/scripts/Dockerfile.ol7 --pull ../sysdig/scripts docker images -q -f 'dangling=true' | xargs --no-run-if-empty docker rmi docker run -i --rm --name ol7-build -v $BASEDIR:/build/probe ol7-builder $PROBE_NAME $PROBE_VERSION $REPOSITORY_NAME OL7-UEK # # AmazonLinux build # echo Building AmazonLinux DIR=$(dirname $(readlink -f $0)) URLS="$($DIR/kernel-crawler.py AmazonLinux)" for URL in $URLS do rhel_build $URL done # # AmazonLinux2 build # echo Building AmazonLinux2 DIR=$(dirname $(readlink -f $0)) URLS="$($DIR/kernel-crawler.py AmazonLinux2)" for URL in $URLS do rhel_build $URL done # # Minikube build # echo Building Minikube VERSIONS=$(curl https://api.github.com/repos/kubernetes/minikube/releases | jq -r '.[].name' | head -10) for VERSION in $VERSIONS do minikube_build $VERSION done # # Upload modules # #if [ $PROBE_NAME = "falco-probe" ]; then # aws s3 sync ./output/ s3://download.draios.com/$REPOSITORY_NAME/sysdig-probe-binaries/ --acl public-read #fi echo "Success." elif [ "RHEL" = "$KERNEL_TYPE" ]; then # # RHEL build from Stored Sources # checkout_sysdig echo Building RHEL from Stored Sources # We're using aql to query for all of the objects in the repo JSON_RESULT=$(curl -H 'Content-Type: text/plain' -H "X-JFrog-Art-Api:$INTERNAL_ARTIFACTORY_API_KEY" -X POST -d 'items.find({"repo":"redhat-sources"})' https://$INTERNAL_ARTIFACTORY_SERVER/artifactory/api/search/aql) # Use jq to parse the json to get the rpms URLS=$(echo "$JSON_RESULT"| jq -r '.results[].name as $o | $o | select(endswith(".rpm")) | $o') for URL in $URLS do rhel_build https://$INTERNAL_ARTIFACTORY_SERVER/artifactory/redhat-sources/$URL $INTERNAL_ARTIFACTORY_API_KEY done elif [ "OL6-UEK" = "$KERNEL_TYPE" ]; then # This should only run in the ol6-builder container context echo Building Oracle Linux 6 UEK DIR=$(dirname $(readlink -f $0)) URLS="$($DIR/oracle-kernel-crawler.py OL6-UEK)" for URL in $URLS do rhel_build $URL done elif [ "OL7-UEK" = "$KERNEL_TYPE" ]; then # This should only run in the ol7-builder container context echo Building Oracle Linux 7 UEK DIR=$(dirname $(readlink -f $0)) URLS="$($DIR/oracle-kernel-crawler.py OL7-UEK)" for URL in $URLS do rhel_build $URL done else exit 1 fi sysdig-0.26.4/scripts/compile-linux-tree.sh000077500000000000000000000047271352731327100206730ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # # # This script tries to compile the sysdig probes against all the stable kernel # releases that match a specific pattern. # # Usage: # # compile-linux-tree.sh SYSDIG_SOURCE_DIRECTORY LINUX_TREE PATTERN1 PATTERN2 ... # # Example: # # compile-linux-tree.sh ~/sysdig_src ~/linux_tree 'v4.1[4-9]*' # set -uo pipefail SYSDIG_SRC_DIR=$1 LINUX_TREE=$2 shift 2 PATTERNS=$@ export KERNELDIR=$LINUX_TREE cd "$LINUX_TREE" TAGS=$(git tag -l $PATTERNS | sort -V) echo "Processing the following versions: $TAGS" for tag in $TAGS do cd "$LINUX_TREE" git checkout "$tag" &> /dev/null if [ $? -ne 0 ]; then echo "$tag -> failure (git)" continue fi make distclean &> /dev/null if [ $? -ne 0 ]; then echo "$tag -> failure (make distclean)" continue fi make defconfig &> /dev/null if [ $? -ne 0 ]; then echo "$tag -> failure (make defconfig)" continue fi make modules_prepare &> /dev/null if [ $? -ne 0 ]; then echo "$tag -> failure (make modules_prepare)" continue fi cd "$SYSDIG_SRC_DIR" make -C driver/bpf clean &> /dev/null rm -rf build mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_BPF=ON .. > /dev/null if [ $? -ne 0 ]; then echo "$tag -> failure (cmake)" continue fi make driver VERBOSE=1 > /dev/null if [ $? -ne 0 ]; then echo "$tag -> failure (driver)" continue fi make bpf VERBOSE=1 > /dev/null if [ $? -ne 0 ]; then echo "$tag -> failure (bpf)" continue fi echo "$tag -> success" done echo "All done" sysdig-0.26.4/scripts/completions/000077500000000000000000000000001352731327100171345ustar00rootroot00000000000000sysdig-0.26.4/scripts/completions/bash/000077500000000000000000000000001352731327100200515ustar00rootroot00000000000000sysdig-0.26.4/scripts/completions/bash/sysdig000066400000000000000000000107001352731327100212740ustar00rootroot00000000000000_sysdig_complete() { local opts=' \ -A \ --print-ascii \ -b \ --print-base64 \ -C \ --file-size \ -cl \ --list-chisels \ -d \ --displayflt \ -D \ --debug \ -e \ --events \ -E \ --exclude-users \ -F \ --fatfile \ -G \ --seconds \ -h \ --help \ -j \ --json \ -k \ --k8s-api \ -L \ --list-events \ -l \ --list \ -lv \ --page-faults \ -P \ --progress \ -q \ --quiet \ -R \ --resolve-ports \ -S \ --summary \ -v \ --verbose \ -x \ --print-hex \ -X \ --print-hex-ascii \ -z \ --compress \ -n \ --numevents \ -p \ --print \ -r \ --read \ -w \ --write \ -W \ --limit \ -s \ --snaplen \ -t \ --timetype \ -c \ --chisel \ -i \ --chisel-info' local cur=${COMP_WORDS[COMP_CWORD]} local prev=${COMP_WORDS[COMP_CWORD-1]} case "$prev" in -c|--chisel|-i|--chisel-info) local chisels="" local detail="Use the -i flag to get detailed information about a specific chisel" while IFS= read -r line do if [[ $line =~ "---" ]]; then # skip lines such as # ----------------- continue; elif [[ -z "$line" ]]; then # empty lines reset the category continue; elif [[ $line =~ "Category" ]]; then # category continue; elif [[ $line =~ $detail ]]; then # detail instructions continue; fi local chisel=${line%% *} if [[ -z "$chisel" ]]; then # empty lines from description continue; fi chisels="$chisels $chisel" done < <(sysdig -cl) COMPREPLY=( $( compgen -W "$chisels" -- $cur ) ) return 0 ;; esac # completing an option if [[ "$cur" == -* ]]; then COMPREPLY=( $( compgen -W "$opts" -- $cur ) ) fi } complete -o default -F _sysdig_complete sysdig # Local Variables: # mode:sh # End: sysdig-0.26.4/scripts/completions/zsh/000077500000000000000000000000001352731327100177405ustar00rootroot00000000000000sysdig-0.26.4/scripts/completions/zsh/_sysdig000066400000000000000000000272661352731327100213410ustar00rootroot00000000000000#compdef sysdig # This completion code is based on the cmdline interface as it exists in sysdig # 0.1.101 # this must match fields_info.cpp local DESCRIPTION_TEXT_START=16 # handles completion of filter fields function _filter () { # I run 'sysdig -l' to get a list of available filter types to generate the # user choice local in_field=0 fields typeset -a fields _call_program chisel_info sysdig -l 2>/dev/null | while IFS='' read -r line; do if [[ $line =~ "^([a-zA-Z0-9_]+\.[a-zA-Z0-9_\.]+) +(.*?)$" ]]; then # starting a new field in_field=1 fields[$#fields+1]="$match[1]:$match[2]" elif (($in_field)) && [[ $line =~ "^ {$DESCRIPTION_TEXT_START}(.*)" ]]; then # field continuation fields[$#fields]+=$match[1] else in_field=0 fi done # Here I use _describe -S '' to omit the closing quote when completing. # Otherwise if you have # # $ sysdig -p '%fd.si # # and you press TAB, you'll get # # $ sysdig -p '%fd.sip' # # Note the trailing ', which I don't want if [[ -z $1 ]]; then # full filter expansion local ops ops=("=" "!=" "<" "<=" ">" ">=" "contains" "and" "or" "not" "(" ")") # Remove the FILTER-ONLY string, since we don't need to indicate that to # the user here fields=(${fields/"(FILTER ONLY) "/}) # _describe -t fields 'Fields' fields _describe -t operators 'Operators' ops elif [[ $1 == "format" ]]; then # expanding format specifiers local allfields_withpercent; # add a leading '%', and remove all FILTER-ONLY fields allfields_withpercent=("%"${^fields:#*FILTER ONLY*}) # skip all leading characters that can't be in a field name. This lets # me specify multiple fields in the string or do things like # "%fd.cip,%fd.cport" compset -P "*[^a-zA-Z0-9_%\.]" _describe -t format_fields 'Format fields' allfields_withpercent -S '' fi } # handles completion for chisel names function _chisels () { # I run 'sysdig -cl' to get a list of available chisels, and build an # _alternative command to generate the user choice local incategory=0 alts_types alts_chisels typeset -a alts_types alts_chisels function closetype() { if [[ -n "$alts_types[$#alts_types]" ]] && (($#alts_chisels)); then # append the trailing " local descriptions_for_type typeset -a descriptions_for_type descriptions_for_type=(${^alts_chisels}\") alts_types[$#alts_types]+=$descriptions_for_type[*]'))' fi alts_chisels=() incategory=0 } _call_program chisels sysdig -cl 2>/dev/null | while IFS='' read -r line; do if [[ $line =~ "^-+$" ]]; then # skip lines such as # ----------------- continue; elif [[ -z "$line" ]]; then # empty lines reset the category closetype elif [[ $line =~ "^Category: (.*)" ]]; then # got a new category closetype incategory=1 local category=$match[1] local index=$(($#alts_types+1)) alts_types[$index]="category_$index:$category:((" elif (($incategory)) && [[ $line =~ "^([a-zA-Z0-9_-]+) *(.+?)" ]]; then # got a chisel local name=$match[1]; local descr=$match[2]; local index=$(($#alts_chisels+1)) alts_chisels[$index]+="${name}\\:\"$descr" # leave out closing " to # allow line # continuations elif (($incategory)) && [[ $line =~ "^ {$DESCRIPTION_TEXT_START}(.*)" ]]; then # field continuation alts_chisels[$#alts_chisels]+=$match[1] fi done closetype _alternative $alts_types[*] } # returns (in $$1) a description of a given argument of a given chisel function _get_chisel_arg () { # I run 'sysdig -i ' to get a description of this chisel. I parse # the argument list at the end of this description. This list looks like # this # # Args: # [int] arg1 - blah blah blah blah blah # [int] arg2 - something blah blah blah blah blah blah blah b # lah blah blah blah blah # # The odd line wrapping is a result of a sysdig bug (patch in review), so # here I assume that this has been fixed local outputvar=$1 chisel=$2 nth_want=$3 local in_arglist=0 in_argwant=0 nth_have=0 _call_program chisel_info sysdig -i $chisel 2>/dev/null | while IFS='' read -r line; do if (($in_arglist)); then # I'm in the argument list if (($in_argwant)); then if [[ $line =~ "^ {$DESCRIPTION_TEXT_START}(.*?)$" ]]; then # I'm in a continuation line for my argument I strip the # leading whitespace and append eval "$outputvar+=\$match[1]" else # Done reading my argument return fi elif [[ $line =~ "^\[" ]] && ((++nth_have == $nth_want)); then # I'm in the argument I want! ((++in_argwant)) eval "$outputvar=\$line"; fi elif [[ $line =~ "^Args:" ]]; then in_arglist=1 continue fi done } # If the cursor is in a chisel argument, returns (in $$1) the description of this # argument. Otherwise returns '' function _in_chiselarg () { local outputvar=$1 local index=$words[(i)-c] local chisel_index=$words[(i)--chisel] if [[ $index -le $CURRENT ]]; then # the -c index is valid ; elif [[ $chisel_index -le $CURRENT ]]; then # the --chisel index is valid index=$chisel_index; else # neither index is valid, so return false return fi # There is a chisel argument somewhere on the commandline. I look to see if # I'm currently typing into it, and return the description if so local chisel=$words[$index+1] local nth=$(($CURRENT-$index-1)) _get_chisel_arg $outputvar $chisel $nth } function _chiselarg () { # the index of the -c/--chisel argument local description=$1 _alternative "chiselarg:$description:" } local context state state_descr line typeset -A opt_args _arguments \ '(-A --print-ascii)'{-A,--print-ascii}'[Only print the text portion of data buffers]' \ '(-b --print-base64)'{-b,--print-base64}'[Print data buffers in base64]' \ '(-C --file-size)'{-C,--file-size=-}'[Chunk captures to files of given size]:Maximum chunk file size:' \ '(-cl --list-chisels)'{-cl,--list-chisels}'[lists the available chisels]' \ '(-d --displayflt)'{-d,--displayflt}'[Make the given filter a display one]' \ '(-D --debug)'{-D,--debug}'[Capture events about sysdig itself]' \ '(-E --exclude-users)'{-E,--exclude-users}'[Don'\''t create the user/group tables when starting]' \ '(-e --events)'{-e,--events}'[Rotate the capture file every events]:Maximum event number:' \ '(-F --fatfile)'{-F,--fatfile}'[Enable fatfile mode]' \ '(-G --seconds)'{-G,--seconds=-}'[Rotate the capture file every seconds]:Rotation period (seconds):' \ '(-h --help)'{-h,--help}'[Print this help]' \ '(-j --json)'{-j,--json}'[Emit output as JSON]' \ '(-k --k8s-api)'{-k,--k8s-api}'[Kubernetes API server]' \ '(-L --list-events)'{-L,--list-events}'[List the events that the engine supports]' \ '(-l -lv --list)'{-l,--list}'[List the fields that can be used for filtering]' \ '(-l -lv --list)-lv[Verbosely list the fields that can be used for filtering]' \ '(-n --numevents)'{-n,--numevents=-}'[Stop capturing after events]:Max events:' \ '--page-faults[Capture user/kernel major/minor page faults]' \ '(-P --progress)'{-P,--progress}'[Print progress on stderr while processing trace files]' \ '(-p --print)'{-p,--print=-}'[Specify the event format (default reported with "sysdig -pp")]:Event output format:->format' \ '(-q --quiet)'{-q,--quiet}'[Do not print events on the screen]' \ '(-r --read)'{-r,--read=-}'[Read events from ]:Input file:_files -g "*.scap"' \ '(-R --resolve-ports)'{-R,--resolve-ports}'[Resolve port numbers to names.]' \ '(-S --summary)'{-S,--summary}'[Print the event summary when the capture ends]' \ '(-s --snaplen)'{-s,--snaplen=-}'[Capture the first bytes of each I/O buffer]:Buffer length (bytes):' \ '(-t --timetype)'{-t,--timetype=-}'[Change the way event time is displayed]:Time-reporting type:(( \ h\:"human-readable string" \ a\:"absolute timestamp from epoch" \ r\:"relative time from capture start" \ d\:"delta between enter/exit" \ D\:"delta from previous event"))' \ '(-v --verbose)'{-v,--verbose}'[Verbose output]' \ '--unbuffered[Disable buffering of output]' \ '(-w --write)'{-w,--write=-}'[Write events to ]:Output file:_files -g "*.scap"' \ '(-W --limit)'{-W,--limit}'[Limit split captures (-C, -G or -e) to a given number of files]:Max # of files' \ '(-x --print-hex)'{-x,--print-hex}'[Print data buffers in hex]' \ '(-X --print-hex-ascii)'{-X,--print-hex-ascii}'[Print data buffers in hex and ASCII]' \ '(-z --compress)'{-z,--compress}'[Enables compression for stored tracefiles]' \ '-pp[Report the default printing format used if -p is omitted]' \ '(-c --chisel)'{-c,--chisel}'[Run a given chisel]:Chisel:->chisel' \ '(-i --chisel-info)'{-i,--chisel-info}'[Get a detailed chisel description]:Chisel:->chisel' \ '*:Filter or chisel argument:->filter_or_chiselarg' && return 0 case $state in (chisel) _chisels ;; (format) _filter format ;; (filter_or_chiselarg) local chiselarg _in_chiselarg chiselarg if [[ -n "$chiselarg" ]]; then _chiselarg $chiselarg else _filter fi ;; esac # Local Variables: # mode:sh # End: sysdig-0.26.4/scripts/debian/000077500000000000000000000000001352731327100160225ustar00rootroot00000000000000sysdig-0.26.4/scripts/debian/postinst.in000077500000000000000000000024701352731327100202430ustar00rootroot00000000000000#!/bin/sh # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # set -e DKMS_PACKAGE_NAME="@PACKAGE_NAME@" DKMS_VERSION="@PROBE_VERSION@" postinst_found=0 case "$1" in configure) for DKMS_POSTINST in /usr/lib/dkms/common.postinst /usr/share/$DKMS_PACKAGE_NAME/postinst; do if [ -f $DKMS_POSTINST ]; then $DKMS_POSTINST $DKMS_PACKAGE_NAME $DKMS_VERSION /usr/share/$DKMS_PACKAGE_NAME "" $2 postinst_found=1 break fi done if [ "$postinst_found" -eq 0 ]; then echo "ERROR: DKMS version is too old and $DKMS_PACKAGE_NAME was not" echo "built with legacy DKMS support." echo "You must either rebuild $DKMS_PACKAGE_NAME with legacy postinst" echo "support or upgrade DKMS to a more current version." exit 1 fi ;; esac sysdig-0.26.4/scripts/debian/prerm.in000077500000000000000000000016121352731327100175020ustar00rootroot00000000000000#!/bin/sh # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # set -e DKMS_PACKAGE_NAME="@PACKAGE_NAME@" DKMS_VERSION="@PROBE_VERSION@" case "$1" in remove|upgrade|deconfigure) if [ "$(dkms status -m $DKMS_PACKAGE_NAME -v $DKMS_VERSION)" ]; then dkms remove -m $DKMS_PACKAGE_NAME -v $DKMS_VERSION --all fi ;; esac sysdig-0.26.4/scripts/description.txt000066400000000000000000000006231352731327100176650ustar00rootroot00000000000000Sysdig instruments your physical and virtual machines at the OS level by installing into the Linux kernel and capturing system calls and other OS events. Then, using sysdig's command line interface, you can filter and decode these events in order to extract useful information. Sysdig can be used to inspect systems live in real-time, or to generate trace files that can be analyzed at a later stage. sysdig-0.26.4/scripts/install-sysdig.in000066400000000000000000000124701352731327100201020ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of _COMPONENT_ . # # 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. # set -e function install_rpm { if ! hash curl > /dev/null 2>&1; then echo "* Installing curl" yum -q -y install curl fi if ! yum -q list dkms > /dev/null 2>&1; then echo "* Installing EPEL repository (for DKMS)" if [ $VERSION -eq 7 ] && [ $DISTRO = "centos" ]; then rpm --quiet -i https://mirrors.kernel.org/centos/7/extras/x86_64/Packages/epel-release-7-11.noarch.rpm elif [ $VERSION -eq 7 ]; then rpm --quiet -i https://mirrors.kernel.org/fedora-epel/7/x86_64/Packages/e/epel-release-7-11.noarch.rpm else rpm --quiet -i https://mirrors.kernel.org/fedora-epel/6/i386/epel-release-6-8.noarch.rpm fi fi echo "* Installing _COMPONENT_ public key" rpm --quiet --import https://s3.amazonaws.com/download.draios.com/DRAIOS-GPG-KEY.public echo "* Installing _COMPONENT_ repository" curl -s -o /etc/yum.repos.d/draios.repo https://s3.amazonaws.com/download.draios.com/_REPOSITORY_NAME_/rpm/draios.repo echo "* Installing kernel headers" KERNEL_VERSION=$(uname -r) if [[ $KERNEL_VERSION == *PAE* ]]; then yum -q -y install kernel-PAE-devel-${KERNEL_VERSION%.PAE} || kernel_warning elif [[ $KERNEL_VERSION == *stab* ]]; then # It's OpenVZ kernel and we should install another package yum -q -y install vzkernel-devel-$KERNEL_VERSION || kernel_warning elif [[ $KERNEL_VERSION == *uek* ]]; then yum -q -y install kernel-uek-devel-$KERNEL_VERSION || kernel_warning else yum -q -y install kernel-devel-$KERNEL_VERSION || kernel_warning fi echo "* Installing _COMPONENT_" yum -q -y install _COMPONENT_ } function install_deb { export DEBIAN_FRONTEND=noninteractive if ! hash curl > /dev/null 2>&1; then echo "* Installing curl" apt-get -qq -y install curl < /dev/null fi echo "* Installing Sysdig public key" curl -s https://s3.amazonaws.com/download.draios.com/DRAIOS-GPG-KEY.public | apt-key add - echo "* Installing _COMPONENT_ repository" curl -s -o /etc/apt/sources.list.d/draios.list https://s3.amazonaws.com/download.draios.com/_REPOSITORY_NAME_/deb/draios.list apt-get -qq update < /dev/null echo "* Installing kernel headers" apt-get -qq -y install linux-headers-$(uname -r) < /dev/null || kernel_warning echo "* Installing _COMPONENT_" apt-get -qq -y install _COMPONENT_ < /dev/null } function unsupported { echo 'Unsupported operating system. Please consider writing to the mailing list at' echo 'https://groups.google.com/forum/#!forum/sysdig or trying the manual' echo 'installation.' exit 1 } function kernel_warning { echo "Unable to find kernel development files for the current kernel version" $(uname -r) echo "This usually means that your system is not up-to-date or you installed a custom kernel version." echo "The installation will continue but you'll need to install these yourself in order to use _COMPONENT_." echo 'Please write to the mailing list at https://groups.google.com/forum/#!forum/sysdig' echo "if you need further assistance." } if [ $(id -u) != 0 ]; then echo "Installer must be run as root (or with sudo)." exit 1 fi echo "* Detecting operating system" ARCH=$(uname -m) if [[ ! $ARCH = *86 ]] && [ ! $ARCH = "x86_64" ] && [ ! $ARCH = "s390x" ]; then unsupported fi if [ $ARCH = "s390x" ]; then echo "------------" echo "WARNING: A Docker container is the only officially supported platform on s390x" echo "------------" fi if [ -f /etc/debian_version ]; then if [ -f /etc/lsb-release ]; then . /etc/lsb-release DISTRO=$DISTRIB_ID VERSION=${DISTRIB_RELEASE%%.*} else DISTRO="Debian" VERSION=$(cat /etc/debian_version | cut -d'.' -f1) fi case "$DISTRO" in "Ubuntu") if [ $VERSION -ge 10 ]; then install_deb else unsupported fi ;; "LinuxMint") if [ $VERSION -ge 9 ]; then install_deb else unsupported fi ;; "Debian") if [ $VERSION -ge 6 ]; then install_deb elif [[ $VERSION == *sid* ]]; then install_deb else unsupported fi ;; *) unsupported ;; esac elif [ -f /etc/system-release-cpe ]; then DISTRO=$(cat /etc/system-release-cpe | cut -d':' -f3) # New Amazon Linux 2 distro if [[ -f /etc/image-id ]]; then AMZ_AMI_VERSION=$(cat /etc/image-id | grep 'image_name' | cut -d"=" -f2 | tr -d "\"") fi if [[ "${DISTRO}" == "o" ]] && [[ ${AMZ_AMI_VERSION} = *"amzn2"* ]]; then DISTRO=$(cat /etc/system-release-cpe | cut -d':' -f4) fi VERSION=$(cat /etc/system-release-cpe | cut -d':' -f5 | cut -d'.' -f1 | sed 's/[^0-9]*//g') case "$DISTRO" in "oracle" | "centos" | "redhat") if [ $VERSION -ge 6 ]; then install_rpm else unsupported fi ;; "amazon") install_rpm ;; "fedoraproject") if [ $VERSION -ge 13 ]; then install_rpm else unsupported fi ;; *) unsupported ;; esac else unsupported fi sysdig-0.26.4/scripts/kernel-crawler.py000077500000000000000000000365771352731327100201140ustar00rootroot00000000000000#!/usr/bin/env python # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # # Author: Samuele Pilleri # Date: August 17th, 2015 import bz2 import re import sqlite3 import sys import tempfile import time import urllib2 import zlib from lxml import html # # This is the main configuration tree for easily analyze Linux repositories # hunting packages. When adding repos or so be sure to respect the same data # structure # repos = { "CentOS" : [ { # This is the root path of the repository in which the script will # look for distros (HTML page) "root" : "http://mirrors.kernel.org/centos/", # This is the XPath + Regex (optional) for analyzing the `root` # page and discover possible distro versions. Use the regex if you # want to limit the version release "discovery_pattern" : "/html/body//pre/a[regex:test(@href, '^6|^7.*$')]/@href", # Once we have found every version available, we need to know were # to go inside the tree to find packages we need (HTML pages) "subdirs" : [ "os/x86_64/Packages/", "updates/x86_64/Packages/" ], # Finally, we need to inspect every page for packages we need. # Again, this is a XPath + Regex query so use the regex if you want # to limit the number of packages reported. "page_pattern" : "/html/body//a[regex:test(@href, '^kernel-(devel-)?[0-9].*\.rpm$')]/@href" }, { "root" : "http://vault.centos.org/", "discovery_pattern" : "//body//table/tr/td/a[regex:test(@href, '^6|^7.*$')]/@href", "subdirs" : [ "os/x86_64/Packages/", "updates/x86_64/Packages/" ], "page_pattern" : "//body//table/tr/td/a[regex:test(@href, '^kernel-(devel-)?[0-9].*\.rpm$')]/@href" }, { "root" : "http://vault.centos.org/centos/", "discovery_pattern" : "//body//table/tr/td/a[regex:test(@href, '^6|^7.*$')]/@href", "subdirs" : [ "os/x86_64/Packages/", "updates/x86_64/Packages/" ], "page_pattern" : "//body//table/tr/td/a[regex:test(@href, '^kernel-(devel-)?[0-9].*\.rpm$')]/@href" } ], "Ubuntu" : [ { # Had to split the URL because, unlikely other repos for which the # script was first created, Ubuntu puts everything into a single # folder. The real URL is be: # http://mirrors.us.kernel.org/ubuntu/pool/main/l/linux/ "root" : "https://mirrors.kernel.org/ubuntu/pool/main/l/", "discovery_pattern" : "/html/body//a[@href = 'linux/']/@href", "subdirs" : [""], "page_pattern" : "/html/body//a[regex:test(@href, '^linux-(image|headers)-(unsigned-)*[3-9].*-generic.*amd64.deb$')]/@href" }, { "root" : "https://mirrors.kernel.org/ubuntu/pool/main/l/", "discovery_pattern" : "/html/body//a[@href = 'linux/']/@href", "subdirs" : [""], "page_pattern" : "/html/body//a[regex:test(@href, '^linux-headers-[3-9].*_all.deb$')]/@href" }, { "root" : "http://security.ubuntu.com/ubuntu/pool/main/l/", "discovery_pattern" : "/html/body//a[@href = 'linux/']/@href", "subdirs" : [""], "page_pattern" : "/html/body//a[regex:test(@href, '^linux-(image|headers)-(unsigned-)*[3-9].*-generic.*amd64.deb$')]/@href" }, { "root" : "http://security.ubuntu.com/ubuntu/pool/main/l/", "discovery_pattern" : "/html/body//a[@href = 'linux/']/@href", "subdirs" : [""], "page_pattern" : "/html/body//a[regex:test(@href, '^linux-headers-[3-9].*_all.deb$')]/@href" }, { "root" : "http://security.ubuntu.com/ubuntu/pool/main/l/", "discovery_pattern" : "/html/body//a[@href = 'linux/']/@href", "subdirs" : [""], "page_pattern" : "/html/body//a[regex:test(@href, '^linux-modules-[3-9].*-generic.*amd64.deb$')]/@href" } ], "Fedora" : [ { "root" : "https://mirrors.kernel.org/fedora/releases/", "discovery_pattern": "/html/body//a[regex:test(@href, '^2[2-9]/$')]/@href", "subdirs" : [ "Everything/x86_64/os/Packages/k/" ], "page_pattern" : "/html/body//a[regex:test(@href, '^kernel-(core|devel)-[0-9].*\.rpm$')]/@href" }, { "root" : "https://mirrors.kernel.org/fedora/updates/", "discovery_pattern": "/html/body//a[regex:test(@href, '^2[2-9]/$')]/@href", "subdirs" : [ "x86_64/Packages/k/" ], "page_pattern" : "/html/body//a[regex:test(@href, '^kernel-(core|devel)-[0-9].*\.rpm$')]/@href" }, { "root" : "https://mirrors.kernel.org/fedora/updates/", "discovery_pattern": "/html/body//a[regex:test(@href, '^2[2-9]/$')]/@href", "subdirs" : [ "Everything/x86_64/Packages/k/" ], "page_pattern" : "/html/body//a[regex:test(@href, '^kernel-(core|devel)-[0-9].*\.rpm$')]/@href" }, # { # "root" : "https://mirrors.kernel.org/fedora/development/", # "discovery_pattern": "/html/body//a[regex:test(@href, '^2[2-9]/$')]/@href", # "subdirs" : [ # "x86_64/os/Packages/k/" # ], # "page_pattern" : "/html/body//a[regex:test(@href, '^kernel-(core|devel)-[0-9].*\.rpm$')]/@href" # } ], # # Fedora Atomic repo is hard-coded to get the 4.17.x and 4.18.x (excluding rc) for now. # "Fedora-Atomic" : [ { "root" : "https://kojipkgs.fedoraproject.org/packages/kernel/", "version_discovery_pattern": "/html/body//a[regex:test(@href, '^4\.1[78].*/$')]/@href", "build_discovery_pattern": "/html/body//a[regex:test(@href, '^[0-9]+\.[^r].*/$')]/@href", "subdirs" : [ "x86_64/" ], "page_pattern" : "/html/body//a[regex:test(@href, '^kernel-(core|devel)-[0-9].*\.rpm$')]/@href", }, ], "CoreOS" : [ { "root" : "http://alpha.release.core-os.net/", "discovery_pattern": "/html/body//a[regex:test(@href, 'amd64-usr')]/@href", "subdirs" : [ "" ], "page_pattern" : "/html/body//a[regex:test(@href, '^[5-9][0-9][0-9]|current|[1][0-9]{3}')]/@href", "exclude_patterns": ["^15\d\d\."] }, { "root" : "http://beta.release.core-os.net/", "discovery_pattern": "/html/body//a[regex:test(@href, 'amd64-usr')]/@href", "subdirs" : [ "" ], "page_pattern" : "/html/body//a[regex:test(@href, '^[5-9][0-9][0-9]|current|[1][0-9]{3}')]/@href" }, { "root" : "http://stable.release.core-os.net/", "discovery_pattern": "/html/body//a[regex:test(@href, 'amd64-usr')]/@href", "subdirs" : [ "" ], "page_pattern" : "/html/body//a[regex:test(@href, '^[4-9][0-9][0-9]|current|[1][0-9]{3}')]/@href" } ], "Debian": [ { "root": "https://mirrors.kernel.org/debian/pool/main/l/", "discovery_pattern": "/html/body/pre/a[@href = 'linux/']/@href", "subdirs": [""], "page_pattern": "/html/body//a[regex:test(@href, '^linux-(image|headers)-[3-9]\.[0-9]+\.[0-9]+.*amd64.deb$')]/@href", "exclude_patterns": ["-rt", "dbg", "trunk", "all", "exp", "unsigned", "cloud-amd64"] }, { "root": "http://security.debian.org/pool/updates/main/l/", "discovery_pattern": "/html/body/table//tr/td/a[@href = 'linux/']/@href", "subdirs": [""], "page_pattern": "/html/body//a[regex:test(@href, '^linux-(image|headers)-[3-9]\.[0-9]+\.[0-9]+.*amd64.deb$')]/@href", "exclude_patterns": ["-rt", "dbg", "trunk", "all", "exp", "unsigned", "cloud-amd64"] }, { "root": "http://mirrors.kernel.org/debian/pool/main/l/", "discovery_pattern": "/html/body/pre/a[@href = 'linux-tools/']/@href", "subdirs": [""], "page_pattern": "/html/body//a[regex:test(@href, '^linux-kbuild-.*amd64.deb$')]/@href", "exclude_patterns": ["-rt", "dbg", "trunk", "all", "exp", "unsigned", "cloud-amd64"] } ] } # Build static list, check here for the last Amazon Linux AMI release: https://aws.amazon.com/amazon-linux-2/faqs/ amazon_linux_builder = [('latest', 'updates'), ('latest', 'main'), ('2017.03', 'updates'), ('2017.03', 'main'), ('2017.09', 'updates'), ('2017.09', 'main'), ('2018.03', 'updates'), ('2018.03', 'main')] amazon_repos = [] for repo_release, release_type in amazon_linux_builder: amazon_repos.append({ "root": "http://repo.us-east-1.amazonaws.com/" + repo_release + "/" + release_type + "/mirror.list", "discovery_pattern": "SELECT * FROM packages WHERE name LIKE 'kernel%'", "subdirs": [""], "page_pattern": "", "exclude_patterns": ["doc", "tools", "headers"] }) repos['AmazonLinux'] = amazon_repos prev_months = 24 now = time.localtime() check_months = [time.localtime(time.mktime((now.tm_year, now.tm_mon - n, 1, 0, 0, 0, 0, 0, 0)))[:2] for n in range(prev_months)] amazon_linux2 = [] for year, month in check_months[:-1]: amazon_linux2.append({ "root": "http://amazonlinux.us-east-1.amazonaws.com/" + str(year) + "." + str(month).zfill(2) + "/core/latest/x86_64/mirror.list", "discovery_pattern": "SELECT * FROM packages WHERE name LIKE 'kernel%'", "subdirs": [""], "page_pattern": "", "exclude_patterns": ["doc", "tools", "headers"] }) repos['AmazonLinux2'] = amazon_linux2 def exclude_patterns(repo, packages, base_url, urls): for rpm in packages: if "exclude_patterns" in repo and any(re.search(x, rpm) for x in repo["exclude_patterns"]): continue else: urls.add(base_url + str(urllib2.unquote(rpm))) def process_al_distro(al_distro_name, current_repo): get_url = urllib2.urlopen(current_repo["root"]).readline() if get_url: if al_distro_name == "AmazonLinux": base_mirror_url = get_url.replace('$basearch','x86_64').replace('\n','') + '/' db_path = "repodata/primary.sqlite.bz2" elif al_distro_name == "AmazonLinux2": base_mirror_url = get_url.replace('\n','') + '/' db_path = "repodata/primary.sqlite.gz" response = urllib2.urlopen(base_mirror_url + db_path) if al_distro_name == "AmazonLinux": decompressed_data = bz2.decompress(response.read()) elif al_distro_name == "AmazonLinux2": decompressed_data = zlib.decompress(response.read(), 16+zlib.MAX_WBITS) db_file = tempfile.NamedTemporaryFile() db_file.write(decompressed_data) conn = sqlite3.connect(db_file.name) conn.row_factory = sqlite3.Row c = conn.cursor() al_rpms = [r["location_href"] for r in c.execute(current_repo["discovery_pattern"])] exclude_patterns(current_repo, al_rpms, base_mirror_url, urls) conn.close() db_file.close() return True else: return False # # Fedora Atomic needs 2 levels of discovery(for version, and build id, respectively) # def process_atomic_distro(current_repos): for repo in current_repos["Fedora-Atomic"]: try: root = urllib2.urlopen(repo["root"],timeout=URL_TIMEOUT).read() except: continue versions = html.fromstring(root).xpath(repo["version_discovery_pattern"], namespaces = {"regex": "http://exslt.org/regular-expressions"}) for version in versions: version_url=repo["root"] + version try: version_page=urllib2.urlopen(version_url,timeout=URL_TIMEOUT).read() except: continue builds = html.fromstring(version_page).xpath(repo["build_discovery_pattern"], namespaces = {"regex": "http://exslt.org/regular-expressions"}) for build in builds: for subdir in repo["subdirs"]: source = version_url + build + subdir try: page = urllib2.urlopen(source,timeout=URL_TIMEOUT).read() except: continue rpms = html.fromstring(page).xpath(repo["page_pattern"], namespaces = {"regex": "http://exslt.org/regular-expressions"}) exclude_patterns(repo, rpms, source, urls) # # In our design you are not supposed to modify the code. The whole script is # created so that you just have to add entry to the `repos` array and new # links will be found automagically without needing to write any single line of # code. # urls = set() URL_TIMEOUT=30 if len(sys.argv) < 2 or not sys.argv[1] in repos: sys.stderr.write("Usage: " + sys.argv[0] + " \n") sys.exit(1) distro = sys.argv[1] # # Navigate the `repos` tree and look for packages we need that match the # patterns given. Save the result in `packages`. # al2_repo_count = 0 for repo in repos[distro]: if distro == 'AmazonLinux': try: process_al_distro(distro, repo) except: continue elif distro == 'AmazonLinux2': try: # Brute force finding the repositories and only grab the most recent two, then skip the rest. if al2_repo_count < 2: if process_al_distro(distro, repo): al2_repo_count += 1 except: continue elif distro == "Fedora-Atomic": try: process_atomic_distro(repos) except: continue else: try: root = urllib2.urlopen(repo["root"],timeout=URL_TIMEOUT).read() except: continue versions = html.fromstring(root).xpath(repo["discovery_pattern"], namespaces = {"regex": "http://exslt.org/regular-expressions"}) for version in versions: for subdir in repo["subdirs"]: # The try - except block is used because 404 errors and similar # might happen (and actually happen because not all repos have # packages we need) try: source = repo["root"] + version + subdir page = urllib2.urlopen(source,timeout=URL_TIMEOUT).read() rpms = html.fromstring(page).xpath(repo["page_pattern"], namespaces = {"regex": "http://exslt.org/regular-expressions"}) exclude_patterns(repo, rpms, source, urls) except: continue # # Print URLs to stdout # for url in urls: print(url) sysdig-0.26.4/scripts/oracle-kernel-crawler.py000077500000000000000000000102771352731327100213440ustar00rootroot00000000000000#!/usr/bin/python # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # import sys import urllib2 from lxml import html # # Copied from kernel-crawler.py and hacked up for oracle linux # because they don't use a normal directory structure. # repos = { # Oracle only puts full isos with unhelpful names on mirrors.kernel.org, so skip it "OL6-UEK": [ { # yum.oracle.com has a bad cert, so use http instead of https "root": "http://yum.oracle.com/", "discovery_pattern": "/html/body//h3/a[regex:test(@href, 'oracle-linux-6\.html')]/@href", "sub_discovery_pattern": "/html/body//h3[regex:test(., '^UEK Release [3-9]:')]/a[regex:test(@href, 'x86_64/index.html')]/@href", "page_pattern": "/html/body//a[regex:test(@href, '^getPackage/kernel-uek-(devel-)?[0-9].*\.rpm$')]/@href", } ], "OL7-UEK": [ { "root": "http://yum.oracle.com/", "discovery_pattern": "/html/body//h3/a[regex:test(@href, 'oracle-linux-7\.html')]/@href", "sub_discovery_pattern": "/html/body//h3[regex:test(., '^UEK Release [3-9]:')]/a[regex:test(@href, 'x86_64/index.html')]/@href", "page_pattern": "/html/body//a[regex:test(@href, '^getPackage/kernel-uek-(devel-)?[0-9].*\.rpm$')]/@href", } ], "Oracle-RHCK": [ { "root": "http://yum.oracle.com/", "discovery_pattern": "/html/body//h3/a[regex:test(@href, 'oracle-linux-[6-7]+\.html')]/@href", "sub_discovery_pattern": "/html/body//h3[regex:test(., '^Latest:')]/a[regex:test(@href, 'x86_64/index.html')]/@href", "page_pattern": "/html/body//a[regex:test(@href, '^getPackage/kernel-(devel-)?[0-9].*\.rpm$')]/@href", } ] } # # In our design you are not supposed to modify the code. The whole script is # created so that you just have to add entry to the `repos` array and new # links will be found automagically without needing to write any single line of # code. # urls = set() URL_TIMEOUT=30 if len(sys.argv) < 2 or not sys.argv[1] in repos: sys.stderr.write("Usage: " + sys.argv[0] + " \n") sys.exit(1) # # Navigate the `repos` tree and look for packages we need that match the # patterns given. Save the result in `packages`. # for repo in repos[sys.argv[1]]: try: root = urllib2.urlopen(repo["root"],timeout=URL_TIMEOUT).read() except: continue versions = html.fromstring(root).xpath(repo["discovery_pattern"], namespaces = {"regex": "http://exslt.org/regular-expressions"}) for version in versions: ver_url = repo["root"] + version try: subroot = urllib2.urlopen(ver_url,timeout=URL_TIMEOUT).read() except: continue sub_vers = html.fromstring(subroot).xpath(repo["sub_discovery_pattern"], namespaces = {"regex": "http://exslt.org/regular-expressions"}) for sub_ver in sub_vers: sub_ver = sub_ver.lstrip('/') # The try - except block is used because 404 errors and similar # might happen (and actually happen because not all repos have # packages we need) try: source = repo["root"] + sub_ver page = urllib2.urlopen(source,timeout=URL_TIMEOUT).read() rpms = html.fromstring(page).xpath(repo["page_pattern"], namespaces = {"regex": "http://exslt.org/regular-expressions"}) source = source.replace("index.html", "") for rpm in rpms: urls.add(source + str(urllib2.unquote(rpm))) except: continue # # Print URLs to stdout # for url in urls: print(url) sysdig-0.26.4/scripts/rpm/000077500000000000000000000000001352731327100153765ustar00rootroot00000000000000sysdig-0.26.4/scripts/rpm/postinstall000077500000000000000000000023121352731327100176760ustar00rootroot00000000000000# # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # dkms add -m sysdig -v %{version} --rpm_safe_upgrade if [ `uname -r | grep -c "BOOT"` -eq 0 ] && [ -e /lib/modules/`uname -r`/build/include ]; then dkms build -m sysdig -v %{version} dkms install --force -m sysdig -v %{version} elif [ `uname -r | grep -c "BOOT"` -gt 0 ]; then echo -e "" echo -e "Module build for the currently running kernel was skipped since you" echo -e "are running a BOOT variant of the kernel." else echo -e "" echo -e "Module build for the currently running kernel was skipped since the" echo -e "kernel source for this kernel does not seem to be installed." fi sysdig-0.26.4/scripts/rpm/preuninstall000077500000000000000000000012661352731327100200510ustar00rootroot00000000000000# # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # dkms remove -m sysdig -v %{version} --all --rpm_safe_upgrade sysdig-0.26.4/scripts/sysdig-probe-loader000077500000000000000000000321311352731327100204010ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # # # Simple script that desperately tries to load sysdig-probe looking # for it in a bunch of ways. Convenient when running sysdig inside # a container or in other weird environments. # # # Returns 1 if $cos_ver > $base_ver, 0 otherwise # cos_version_greater() { if [[ $cos_ver == $base_ver ]]; then return 0 fi # # COS build numbers are in the format x.y.z # a=`echo $cos_ver | cut -d. -f1` b=`echo $cos_ver | cut -d. -f2` c=`echo $cos_ver | cut -d. -f3` d=`echo $base_ver | cut -d. -f1` e=`echo $base_ver | cut -d. -f2` f=`echo $base_ver | cut -d. -f3` # Test the first component if [[ $a -gt $d ]]; then return 1 elif [[ $d -gt $a ]]; then return 0 fi # Test the second component if [[ $b -gt $e ]]; then return 1 elif [[ $e -gt $b ]]; then return 0 fi # Test the third component if [[ $c -gt $f ]]; then return 1 elif [[ $f -gt $c ]]; then return 0 fi # If we get here, probably malformatted version string? return 0 } get_kernel_config() { if [ -f /proc/config.gz ]; then echo "Found kernel config at /proc/config.gz" KERNEL_CONFIG_PATH=/proc/config.gz elif [ -f "/boot/config-${KERNEL_RELEASE}" ]; then echo "Found kernel config at /boot/config-${KERNEL_RELEASE}" KERNEL_CONFIG_PATH=/boot/config-${KERNEL_RELEASE} elif [ ! -z "${SYSDIG_HOST_ROOT}" ] && [ -f "${SYSDIG_HOST_ROOT}/boot/config-${KERNEL_RELEASE}" ]; then echo "Found kernel config at ${SYSDIG_HOST_ROOT}/boot/config-${KERNEL_RELEASE}" KERNEL_CONFIG_PATH="${SYSDIG_HOST_ROOT}/boot/config-${KERNEL_RELEASE}" elif [ -f "/usr/lib/ostree-boot/config-${KERNEL_RELEASE}" ]; then echo "Found kernel config at /usr/lib/ostree-boot/config-${KERNEL_RELEASE}" KERNEL_CONFIG_PATH="/usr/lib/ostree-boot/config-${KERNEL_RELEASE}" elif [ ! -z "${SYSDIG_HOST_ROOT}" ] && [ -f "${SYSDIG_HOST_ROOT}/usr/lib/ostree-boot/config-${KERNEL_RELEASE}" ]; then echo "Found kernel config at ${SYSDIG_HOST_ROOT}/usr/lib/ostree-boot/config-${KERNEL_RELEASE}" KERNEL_CONFIG_PATH="${SYSDIG_HOST_ROOT}/usr/lib/ostree-boot/config-${KERNEL_RELEASE}" elif [ -f /lib/modules/${KERNEL_RELEASE}/config ]; then # this code works both for native host and agent container assuming that # Dockerfile sets up the desired symlink /lib/modules -> $SYSDIG_HOST_ROOT/lib/modules echo "Found kernel config at /lib/modules/${KERNEL_RELEASE}/config" KERNEL_CONFIG_PATH="/lib/modules/${KERNEL_RELEASE}/config" fi if [ -z "${KERNEL_CONFIG_PATH}" ]; then echo "Cannot find kernel config" exit 1 fi if [[ "${KERNEL_CONFIG_PATH}" == *.gz ]]; then HASH=$(zcat "${KERNEL_CONFIG_PATH}" | md5sum - | cut -d' ' -f1) else HASH=$(md5sum "${KERNEL_CONFIG_PATH}" | cut -d' ' -f1) fi } load_kernel_probe() { if ! hash lsmod > /dev/null 2>&1; then echo "This program requires lsmod" exit 1 fi if ! hash modprobe > /dev/null 2>&1; then echo "This program requires modprobe" exit 1 fi if ! hash rmmod > /dev/null 2>&1; then echo "This program requires rmmod" exit 1 fi echo "* Unloading ${PROBE_NAME}, if present" rmmod "${PROBE_NAME}" 2>/dev/null WAIT_TIME=0 KMOD_NAME=$(echo "${PROBE_NAME}" | tr "-" "_") while lsmod | grep "${KMOD_NAME}" > /dev/null 2>&1 && [ $WAIT_TIME -lt $MAX_RMMOD_WAIT ]; do if rmmod "${PROBE_NAME}" 2>/dev/null; then echo "* Unloading ${PROBE_NAME} succeeded after ${WAIT_TIME}s" break fi ((++WAIT_TIME)) if (( $WAIT_TIME % 5 == 0 )); then echo "* ${PROBE_NAME} still loaded, waited ${WAIT_TIME}s (max wait ${MAX_RMMOD_WAIT}s)" fi sleep 1 done if lsmod | grep "${KMOD_NAME}" > /dev/null 2>&1; then echo "* ${PROBE_NAME} seems to still be loaded, hoping the best" exit 0 fi # skip dkms on UEK hosts because it will always fail if [[ $(uname -r) == *uek* ]]; then echo "* Skipping dkms install for UEK host" else echo "* Running dkms install for ${PACKAGE_NAME}" if dkms install -m "${PACKAGE_NAME}" -v "${SYSDIG_VERSION}" -k "${KERNEL_RELEASE}"; then echo "* Trying to load a dkms ${PROBE_NAME}, if present" if insmod "/var/lib/dkms/${PACKAGE_NAME}/${SYSDIG_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${PROBE_NAME}.ko" > /dev/null 2>&1; then echo "${PROBE_NAME} found and loaded in dkms" exit 0 elif insmod "/var/lib/dkms/${PACKAGE_NAME}/${SYSDIG_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${PROBE_NAME}.ko.xz" > /dev/null 2>&1; then echo "${PROBE_NAME} found and loaded in dkms (xz)" exit 0 else echo "* Unable to insmod" fi else DKMS_LOG="/var/lib/dkms/${PACKAGE_NAME}/${SYSDIG_VERSION}/build/make.log" if [ -f "${DKMS_LOG}" ]; then echo "* Running dkms build failed, dumping ${DKMS_LOG}" cat "${DKMS_LOG}" else echo "* Running dkms build failed, couldn't find ${DKMS_LOG}" fi fi fi echo "* Trying to load a system ${PROBE_NAME}, if present" if modprobe "${PROBE_NAME}" > /dev/null 2>&1; then echo "${PROBE_NAME} found and loaded with modprobe" exit 0 fi echo "* Trying to find precompiled ${PROBE_NAME} for ${KERNEL_RELEASE}" get_kernel_config local SYSDIG_PROBE_FILENAME="${PROBE_NAME}-${SYSDIG_VERSION}-${ARCH}-${KERNEL_RELEASE}-${HASH}.ko" if [ -f "${HOME}/.sysdig/${SYSDIG_PROBE_FILENAME}" ]; then echo "Found precompiled module at ~/.sysdig/${SYSDIG_PROBE_FILENAME}, loading module" insmod "${HOME}/.sysdig/${SYSDIG_PROBE_FILENAME}" exit $? fi local URL URL=$(echo "${SYSDIG_PROBE_URL}/${SYSDIG_REPOSITORY}/sysdig-probe-binaries/${SYSDIG_PROBE_FILENAME}" | sed s/+/%2B/g) echo "* Trying to download precompiled module from ${URL}" if curl --create-dirs "${SYSDIG_PROBE_CURL_OPTIONS}" -o "${HOME}/.sysdig/${SYSDIG_PROBE_FILENAME}" "${URL}"; then echo "Download succeeded, loading module" insmod "${HOME}/.sysdig/${SYSDIG_PROBE_FILENAME}" exit $? else echo "Download failed, consider compiling your own ${PROBE_NAME} and loading it or getting in touch with the sysdig community" exit 1 fi } load_bpf_probe() { echo "* Mounting debugfs" if [ ! -d /sys/kernel/debug/tracing ]; then mount -t debugfs nodev /sys/kernel/debug fi get_kernel_config if [ ! -z "${SYSDIG_HOST_ROOT}" ] && [ -f "${SYSDIG_HOST_ROOT}/etc/os-release" ]; then . "${SYSDIG_HOST_ROOT}/etc/os-release" if [ "${ID}" == "cos" ]; then COS=1 fi fi if [ ! -z "${SYSDIG_HOST_ROOT}" ] && [ -f "${SYSDIG_HOST_ROOT}/etc/VERSION" ]; then MINIKUBE=1 MINIKUBE_VERSION="$(cat ${SYSDIG_HOST_ROOT}/etc/VERSION)" fi local BPF_PROBE_FILENAME="${BPF_PROBE_NAME}-${SYSDIG_VERSION}-${ARCH}-${KERNEL_RELEASE}-${HASH}.o" if [ ! -f "${HOME}/.sysdig/${BPF_PROBE_FILENAME}" ]; then local BPF_KERNEL_SOURCES_URL="" local STRIP_COMPONENTS=1 customize_kernel_build() { if [ -n "${KERNEL_EXTRA_VERSION}" ]; then sed -i "s/LOCALVERSION=\"\"/LOCALVERSION=\"${KERNEL_EXTRA_VERSION}\"/" .config fi make olddefconfig > /dev/null make modules_prepare > /dev/null } if [ -n "${COS}" ]; then echo "* COS detected (build ${BUILD_ID}), using cos kernel headers..." BPF_KERNEL_SOURCES_URL="https://storage.googleapis.com/cos-tools/${BUILD_ID}/kernel-headers.tgz" KERNEL_EXTRA_VERSION="+" STRIP_COMPONENTS=0 customize_kernel_build() { pushd usr/src/* > /dev/null # Note: this overrides the KERNELDIR set while untarring the tarball export KERNELDIR=`pwd` sed -i '/^#define randomized_struct_fields_start struct {$/d' include/linux/compiler-clang.h sed -i '/^#define randomized_struct_fields_end };$/d' include/linux/compiler-clang.h popd > /dev/null # Might need to configure our own sources depending on COS version cos_ver=${BUILD_ID} base_ver=11553.0.0 cos_version_greater greater_ret=$? if [[ greater_ret -eq 1 ]]; then export KBUILD_EXTRA_CPPFLAGS=-DCOS_73_WORKAROUND fi } fi if [ -n "${MINIKUBE}" ]; then echo "* Minikube detected (${MINIKUBE_VERSION}), using linux kernel sources for minikube kernel" local kernel_version=$(uname -r) local -r kernel_version_major=$(echo ${kernel_version} | cut -d. -f1) local -r kernel_version_minor=$(echo ${kernel_version} | cut -d. -f2) local -r kernel_version_patch=$(echo ${kernel_version} | cut -d. -f3) if [ "${kernel_version_patch}" == "0" ]; then kernel_version="${kernel_version_major}.${kernel_version_minor}" fi BPF_KERNEL_SOURCES_URL="http://mirrors.edge.kernel.org/pub/linux/kernel/v${kernel_version_major}.x/linux-${kernel_version}.tar.gz" fi if [ -n "${SYSDIG_BPF_USE_LOCAL_KERNEL_SOURCES}" ]; then local -r kernel_version_major=$(uname -r | cut -d. -f1) local -r kernel_version=$(uname -r | cut -d- -f1) KERNEL_EXTRA_VERSION="-$(uname -r | cut -d- -f2)" echo "* Using downloaded kernel sources for kernel version ${kernel_version}..." BPF_KERNEL_SOURCES_URL="http://mirrors.edge.kernel.org/pub/linux/kernel/v${kernel_version_major}.x/linux-${kernel_version}.tar.gz" fi if [ -n "${BPF_KERNEL_SOURCES_URL}" ]; then echo "* Downloading ${BPF_KERNEL_SOURCES_URL}" mkdir -p /tmp/kernel cd /tmp/kernel cd `mktemp -d -p /tmp/kernel` if ! curl -o kernel-sources.tgz --create-dirs "${SYSDIG_PROBE_CURL_OPTIONS}" "${BPF_KERNEL_SOURCES_URL}"; then exit 1; fi echo "* Extracting kernel sources" mkdir kernel-sources && tar xf kernel-sources.tgz -C kernel-sources --strip-components "${STRIP_COMPONENTS}" cd kernel-sources export KERNELDIR=`pwd` if [[ "${KERNEL_CONFIG_PATH}" == *.gz ]]; then zcat "${KERNEL_CONFIG_PATH}" > .config else cat "${KERNEL_CONFIG_PATH}" > .config fi echo "* Configuring kernel" customize_kernel_build fi echo "* Trying to compile BPF probe ${BPF_PROBE_NAME} (${BPF_PROBE_FILENAME})" make -C "/usr/src/${PACKAGE_NAME}-${SYSDIG_VERSION}/bpf" > /dev/null mkdir -p ~/.sysdig mv "/usr/src/${PACKAGE_NAME}-${SYSDIG_VERSION}/bpf/probe.o" "${HOME}/.sysdig/${BPF_PROBE_FILENAME}" if [ -n "${BPF_KERNEL_SOURCES_URL}" ]; then rm -r /tmp/kernel fi fi if [ ! -f "${HOME}/.sysdig/${BPF_PROBE_FILENAME}" ]; then local URL URL=$(echo "${SYSDIG_PROBE_URL}/${SYSDIG_REPOSITORY}/sysdig-probe-binaries/${BPF_PROBE_FILENAME}" | sed s/+/%2B/g) echo "* Trying to download precompiled BPF probe from ${URL}" curl --create-dirs "${SYSDIG_PROBE_CURL_OPTIONS}" -o "${HOME}/.sysdig/${BPF_PROBE_FILENAME}" "${URL}" fi if [ -f "${HOME}/.sysdig/${BPF_PROBE_FILENAME}" ]; then if [ ! -f /proc/sys/net/core/bpf_jit_enable ]; then echo "**********************************************************" echo "** BPF doesn't have JIT enabled, performance might be **" echo "** degraded. Please ensure to run on a kernel with **" echo "** CONFIG_BPF_JIT enabled and/or use --net=host if **" echo "** running inside a container. **" echo "**********************************************************" fi echo "* BPF probe located, it's now possible to start sysdig" ln -sf "${HOME}/.sysdig/${BPF_PROBE_FILENAME}" "${HOME}/.sysdig/${BPF_PROBE_NAME}.o" exit $? else echo "* Failure to find a BPF probe" exit 1 fi } ARCH=$(uname -m) KERNEL_RELEASE=$(uname -r) SCRIPT_NAME=$(basename "${0}") SYSDIG_PROBE_URL=${SYSDIG_PROBE_URL:-https://s3.amazonaws.com/download.draios.com} if [ -n "$SYSDIG_PROBE_INSECURE_DOWNLOAD" ] then SYSDIG_PROBE_CURL_OPTIONS=-fsSk else SYSDIG_PROBE_CURL_OPTIONS=-fsS fi MAX_RMMOD_WAIT=60 if [[ $# -ge 1 ]]; then MAX_RMMOD_WAIT=$1 fi if [ -z "${SYSDIG_REPOSITORY}" ]; then SYSDIG_REPOSITORY="stable" fi if [ "${SCRIPT_NAME}" = "sysdig-probe-loader" ]; then if [ -z "$SYSDIG_VERSION" ]; then SYSDIG_VERSION=$(sysdig --version | cut -d' ' -f3) fi PROBE_NAME="sysdig-probe" BPF_PROBE_NAME="sysdig-probe-bpf" PACKAGE_NAME="sysdig" elif [ "${SCRIPT_NAME}" = "sysdigcloud-probe-loader" ]; then EXEPATH=$(dirname "$(readlink -f "${0}")") if [ -z "$SYSDIG_VERSION" ]; then SYSDIG_VERSION=$("${EXEPATH}"/dragent --version) fi PROBE_NAME="sysdigcloud-probe" BPF_PROBE_NAME="sysdigcloud-probe-bpf" PACKAGE_NAME="draios-agent" elif [ "${SCRIPT_NAME}" = "falco-probe-loader" ]; then if [ -z "$SYSDIG_VERSION" ]; then SYSDIG_VERSION=$(falco --version | cut -d' ' -f3) fi PROBE_NAME="falco-probe" BPF_PROBE_NAME="falco-probe-bpf" PACKAGE_NAME="falco" else echo "This script must be called as sysdig-probe-loader, sysdigcloud-probe-loader, or falco-probe-loader" exit 1 fi if [ "$(id -u)" != 0 ]; then echo "Installer must be run as root (or with sudo)." exit 1 fi if ! hash curl > /dev/null 2>&1; then echo "This program requires curl" exit 1 fi if [ -v SYSDIG_BPF_PROBE ] || [ "${1}" = "bpf" ]; then load_bpf_probe else load_kernel_probe fi sysdig-0.26.4/test/000077500000000000000000000000001352731327100140705ustar00rootroot00000000000000sysdig-0.26.4/test/csysdig_trace_regression.sh000066400000000000000000000077471352731327100215260ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # #set -eu BASEDIR=. SYSDIG=$1 CHISELS=$2 #TMPBASE=${4:-$(mktemp -d --tmpdir sysdig.XXXXXXXXXX)} TMPBASE=. TRACEDIR="${TMPBASE}/traces" RESULTDIR="${TMPBASE}/results" BASELINEDIR="${TMPBASE}/baseline" BRANCH=$3 if [ ! -d "$TRACEDIR" ]; then mkdir -p $TRACEDIR cd $TRACEDIR wget https://s3.amazonaws.com/download.draios.com/sysdig-tests/traces.zip unzip traces.zip rm -rf traces.zip cd - fi if [ ! -d "$BASELINEDIR" ]; then mkdir -p $BASELINEDIR cd $BASELINEDIR wget -O baseline.zip https://s3.amazonaws.com/download.draios.com/sysdig-tests/baseline-$BRANCH.zip || wget -O baseline.zip https://s3.amazonaws.com/download.draios.com/sysdig-tests/baseline-dev.zip unzip baseline.zip rm -rf baseline.zip cd - fi echo "Executing sysdig tests in ${TMPBASE}" ret=0 # Views to run $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vprocs" $TRACEDIR $RESULTDIR/procs $BASELINEDIR/procs || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vfiles" $TRACEDIR $RESULTDIR/files $BASELINEDIR/files || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vconnections" $TRACEDIR $RESULTDIR/connections $BASELINEDIR/connections || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vcontainer_errors" $TRACEDIR $RESULTDIR/container_errors $BASELINEDIR/container_errors || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vcontainers" $TRACEDIR $RESULTDIR/containers $BASELINEDIR/containers || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vdirectories" $TRACEDIR $RESULTDIR/directories $BASELINEDIR/directories || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -verrors" $TRACEDIR $RESULTDIR/errors $BASELINEDIR/errors || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vfile_opens" $TRACEDIR $RESULTDIR/file_opens $BASELINEDIR/file_opens || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vio_by_type" $TRACEDIR $RESULTDIR/io_by_type $BASELINEDIR/io_by_type || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vincoming_connections" $TRACEDIR $RESULTDIR/incoming_connections $BASELINEDIR/incoming_connections || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vincoming_connections" $TRACEDIR $RESULTDIR/incoming_connections $BASELINEDIR/incoming_connections || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vpage_faults" $TRACEDIR $RESULTDIR/page_faults $BASELINEDIR/page_faults || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vprocs_cpu" $TRACEDIR $RESULTDIR/procs_cpu $BASELINEDIR/procs_cpu || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vprocs_errors" $TRACEDIR $RESULTDIR/procs_errors $BASELINEDIR/procs_errors || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vprocs_fd_usage" $TRACEDIR $RESULTDIR/procs_fd_usage $BASELINEDIR/procs_fd_usage || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vsports" $TRACEDIR $RESULTDIR/sports $BASELINEDIR/sports || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vspy_syslog" $TRACEDIR $RESULTDIR/spy_syslog $BASELINEDIR/spy_syslog || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vspy_users" $TRACEDIR $RESULTDIR/spy_users $BASELINEDIR/spy_users || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "--raw -vsyscalls" $TRACEDIR $RESULTDIR/syscalls $BASELINEDIR/syscalls || ret=1 #rm -rf "${TMPBASE}" exit $ret sysdig-0.26.4/test/sysdig_batch_parser.sh000077500000000000000000000042071352731327100204510ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # # # This script runs sysdig on all the trace files (i.e. all the files with scap # extension) in the current directory, and compares the result with the one of # a previous run. # # Arguments: # - sysdig path # - sysdig chisels directory # - sysdig command line # - traces directory # - prefix of the result directory (it will be completed with the current # date/time) # - directory to use as a reference # # Examples: # ./sysdig_batch_parser.sh "-p%thread.exectime" exetime exetime_2014-04-28_10-18-30 # ./sysdig_batch_parser.sh "-ctopconns" topconns topconns_2014-04-28_02-51-34 # # Note: # if the comparison succeeds, the result directory is deleted. Otherwise, it's # kept there for reference/analysis. # set -eu SYSDIG=$1 SYSDIG_CHISEL_DIR=$2 ARGS=$3 TRACESDIR=$4 DIRNAME=$5 REFERENCEDIR=$6 export SYSDIG_CHISEL_DIR unamestr=`uname` if [[ "$unamestr" == 'Linux' ]]; then TIMEOUT_BIN="timeout" elif [[ "$unamestr" == 'Darwin' ]]; then TIMEOUT_BIN="gtimeout" fi rm -rf $DIRNAME || true mkdir -p $DIRNAME if [ ! -e $REFERENCEDIR ]; then echo "Reference directory $REFERENCEDIR does not exist--skipping directory entirely" exit 0 fi for f in $TRACESDIR/* do ref=$REFERENCEDIR/$(basename $f).output; if [ ! -e $ref ]; then echo "Corresponding reference file $ref does not exist--skipping" else echo "Processing $f" TZ=UTC eval ${TIMEOUT_BIN} 60 $SYSDIG -r $f $ARGS > $DIRNAME/$(basename $f).output fi done echo Data saved in $DIRNAME diff -r $DIRNAME $REFERENCEDIR rm -rf $DIRNAME sysdig-0.26.4/test/sysdig_trace_regression.sh000077500000000000000000000255611352731327100213600ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # set -exu unamestr=`uname` if [[ "$unamestr" == 'Linux' ]]; then SCRIPT=$(readlink -f $0) TMPBASE=${4:-$(mktemp -d --tmpdir sysdig.XXXXXXXXXX)} elif [[ "$unamestr" == 'Darwin' ]]; then SCRIPT=$(greadlink -f $0) unset TMPDIR #make shure that mktemp on mac will generate the folder under /tmp TMPBASE=${4:-$(mktemp -d -t sysdig)} fi BASEDIR=$(dirname $SCRIPT) SYSDIG=$1 CHISELS=$2 TRACEDIR="${TMPBASE}/traces" RESULTDIR="${TMPBASE}/results" BASELINEDIR="${TMPBASE}/baseline" BRANCH=$3 if [ ! -d "$TRACEDIR" ]; then mkdir -p $TRACEDIR cd $TRACEDIR wget -O traces.zip https://s3.amazonaws.com/download.draios.com/sysdig-tests/traces-$BRANCH.zip || wget -O traces.zip https://s3.amazonaws.com/download.draios.com/sysdig-tests/traces.zip unzip traces.zip rm -rf traces.zip cd - fi if [ ! -d "$BASELINEDIR" ]; then mkdir -p $BASELINEDIR cd $BASELINEDIR wget -O baseline.zip https://s3.amazonaws.com/download.draios.com/sysdig-tests/baseline-$BRANCH.zip || wget -O baseline.zip https://s3.amazonaws.com/download.draios.com/sysdig-tests/baseline-dev.zip unzip baseline.zip rm -rf baseline.zip cd - fi echo "Executing sysdig tests in ${TMPBASE}" ret=0 # Fields $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-n 1000" $TRACEDIR $RESULTDIR/default $BASELINEDIR/default || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-n 1000 -pc" $TRACEDIR $RESULTDIR/containers $BASELINEDIR/containers || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-n 1000 -t h" $TRACEDIR $RESULTDIR/time_human $BASELINEDIR/time_human || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-n 1000 -t a" $TRACEDIR $RESULTDIR/time_absolute $BASELINEDIR/time_absolute || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-n 1000 -t r" $TRACEDIR $RESULTDIR/time_relative $BASELINEDIR/time_relative || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-n 1000 -t d" $TRACEDIR $RESULTDIR/delta_enter $BASELINEDIR/delta_enter || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-n 1000 -t D" $TRACEDIR $RESULTDIR/delta_previous $BASELINEDIR/delta_previous || ret=1 # Category: CPU Usage $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopcontainers_cpu" $TRACEDIR $RESULTDIR/topcontainers_cpu $BASELINEDIR/topcontainers_cpu || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopprocs_cpu" $TRACEDIR $RESULTDIR/topprocs_cpu $BASELINEDIR/topprocs_cpu || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-pc -ctopprocs_cpu" $TRACEDIR $RESULTDIR/topprocs_cpu_container $BASELINEDIR/topprocs_cpu_container || ret=1 # Category: Errors $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopcontainers_error" $TRACEDIR $RESULTDIR/topcontainers_error $BASELINEDIR/topcontainers_error || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopfiles_errors" $TRACEDIR $RESULTDIR/topfiles_errors $BASELINEDIR/topfiles_errors || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-pc -ctopfiles_errors" $TRACEDIR $RESULTDIR/topfiles_errors_container $BASELINEDIR/topfiles_errors_container || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopprocs_errors" $TRACEDIR $RESULTDIR/topprocs_errors $BASELINEDIR/topprocs_errors || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-pc -ctopprocs_errors" $TRACEDIR $RESULTDIR/topprocs_errors_container $BASELINEDIR/topprocs_errors_container || ret=1 # Category: I/O $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cecho_fds" $TRACEDIR $RESULTDIR/echo_fds $BASELINEDIR/echo_fds || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-pc -cecho_fds" $TRACEDIR $RESULTDIR/echo_fds_container $BASELINEDIR/echo_fds_container || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cfdbytes_by fd.name" $TRACEDIR $RESULTDIR/fdbytes_by $BASELINEDIR/fdbytes_by || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cfdcount_by fd.name" $TRACEDIR $RESULTDIR/fdcount_by $BASELINEDIR/fdcount_by || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ciobytes" $TRACEDIR $RESULTDIR/iobytes $BASELINEDIR/iobytes || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ciobytes_file" $TRACEDIR $RESULTDIR/iobytes_file $BASELINEDIR/iobytes_file || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cspy_file" $TRACEDIR $RESULTDIR/spy_file $BASELINEDIR/spy_file || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cstderr" $TRACEDIR $RESULTDIR/stderr $BASELINEDIR/stderr || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-pc -cstderr" $TRACEDIR $RESULTDIR/stderr_container $BASELINEDIR/stderr_container || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cstdin" $TRACEDIR $RESULTDIR/stdin $BASELINEDIR/stdin || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cstdout" $TRACEDIR $RESULTDIR/stdout $BASELINEDIR/stdout || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-pc -ctopcontainers_file" $TRACEDIR $RESULTDIR/topcontainers_file $BASELINEDIR/topcontainers_file || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopfiles_bytes" $TRACEDIR $RESULTDIR/topfiles_bytes $BASELINEDIR/topfiles_bytes || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-pc -ctopfiles_bytes" $TRACEDIR $RESULTDIR/topfiles_bytes_container $BASELINEDIR/topfiles_bytes_container || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopfiles_time" $TRACEDIR $RESULTDIR/topfiles_time $BASELINEDIR/topfiles_time || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-pc -ctopfiles_time" $TRACEDIR $RESULTDIR/topfiles_time_container $BASELINEDIR/topfiles_time_container || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopprocs_file" $TRACEDIR $RESULTDIR/topprocs_file $BASELINEDIR/topprocs_file || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-pc -ctopprocs_file" $TRACEDIR $RESULTDIR/topprocs_file_container $BASELINEDIR/topprocs_file_container || ret=1 # Category: Logs $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cspy_logs" $TRACEDIR $RESULTDIR/spy_logs $BASELINEDIR/spy_logs || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-pc -cspy_logs" $TRACEDIR $RESULTDIR/spy_logs_container $BASELINEDIR/spy_logs_container || ret=1 # Category: Net $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ciobytes_net" $TRACEDIR $RESULTDIR/iobytes_net $BASELINEDIR/iobytes_net || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cspy_ip 127.0.0.1" $TRACEDIR $RESULTDIR/spy_ip $BASELINEDIR/spy_ip || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cspy_port 80" $TRACEDIR $RESULTDIR/spy_port $BASELINEDIR/spy_port || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopconns" $TRACEDIR $RESULTDIR/topconns $BASELINEDIR/topconns || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-pc -ctopconns" $TRACEDIR $RESULTDIR/topconns_container $BASELINEDIR/topconns_container || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopcontainers_net" $TRACEDIR $RESULTDIR/topcontainers_net $BASELINEDIR/topcontainers_net || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopports_server" $TRACEDIR $RESULTDIR/topports_server $BASELINEDIR/topports_server || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopprocs_net" $TRACEDIR $RESULTDIR/topprocs_net $BASELINEDIR/topprocs_net || ret=1 # Category: Performance $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cbottlenecks" $TRACEDIR $RESULTDIR/bottlenecks $BASELINEDIR/bottlenecks || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cfileslower 1000" $TRACEDIR $RESULTDIR/fileslower $BASELINEDIR/fileslower || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cnetlower 10" $TRACEDIR $RESULTDIR/netlower $BASELINEDIR/netlower || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cproc_exec_time" $TRACEDIR $RESULTDIR/proc_exec_time $BASELINEDIR/proc_exec_time || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cscallslower 1000" $TRACEDIR $RESULTDIR/scallslower $BASELINEDIR/scallslower || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopscalls" $TRACEDIR $RESULTDIR/topscalls $BASELINEDIR/topscalls || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopscalls_time" $TRACEDIR $RESULTDIR/topscalls_time $BASELINEDIR/topscalls_time || ret=1 # Category: Security $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-clist_login_shells" $TRACEDIR $RESULTDIR/list_login_shells $BASELINEDIR/list_login_shells || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cspy_users" $TRACEDIR $RESULTDIR/spy_users $BASELINEDIR/spy_users || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-pc -cspy_users" $TRACEDIR $RESULTDIR/spy_users_container $BASELINEDIR/spy_users_container || ret=1 # Category: System State # $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-clscontainers" $TRACEDIR $RESULTDIR/lscontainers $BASELINEDIR/lscontainers || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-clsof" $TRACEDIR $RESULTDIR/lsof $BASELINEDIR/lsof || ret=1 # $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cnetstat" $TRACEDIR $RESULTDIR/netstat $BASELINEDIR/netstat || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cps" $TRACEDIR $RESULTDIR/ps $BASELINEDIR/ps || ret=1 # JSON $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-j -n 10000" $TRACEDIR $RESULTDIR/fd_fields_json $BASELINEDIR/fd_fields_json || ret=1 # Sessions $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-p '*%evt.num %evt.outputtime %evt.cpu %proc.name (%thread.tid) %evt.dir %evt.type %evt.info sid=%proc.sid sname=%proc.sname'" $TRACEDIR $RESULTDIR/sessions $BASELINEDIR/sessions || ret=1 # Cwd $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-pc -p\"*%evt.num %evt.outputtime %evt.cpu %container.name (%container.id) %proc.name (%thread.tid:%thread.vtid) %evt.dir %evt.type %evt.info %proc.cwd\"" $TRACEDIR $RESULTDIR/cwd $BASELINEDIR/cwd || ret=1 # Testing filters/outputs that can traverse parent thread state $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-p\"*%evt.num %evt.outputtime %evt.cpu %proc.name (%thread.tid) %evt.dir %evt.type %evt.info LS=%proc.loginshellid\"" $TRACEDIR $RESULTDIR/loginshell-parent-loop $BASELINEDIR/loginshell-parent-loop || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "proc.apid=10 or proc.apid=26890" $TRACEDIR $RESULTDIR/apid-parent-loop $BASELINEDIR/apid-parent-loop || ret=1 $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "proc.aname=foo or proc.aname=sh" $TRACEDIR $RESULTDIR/aname-parent-loop $BASELINEDIR/aname-parent-loop || ret=1 rm -rf "${TMPBASE}" exit $ret sysdig-0.26.4/userspace/000077500000000000000000000000001352731327100151035ustar00rootroot00000000000000sysdig-0.26.4/userspace/.gitignore000066400000000000000000000000051352731327100170660ustar00rootroot00000000000000test sysdig-0.26.4/userspace/async/000077500000000000000000000000001352731327100162205ustar00rootroot00000000000000sysdig-0.26.4/userspace/async/async_key_value_source.h000066400000000000000000000272031352731327100231360ustar00rootroot00000000000000/* Copyright (C) 2019 Sysdig, Inc. This file is part of sysdig. 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. */ #pragma once #include #include #include #include #include #include #include #include #include #include namespace sysdig { /** * Base class for classes that need to collect values asynchronously from some * value source. Subclasses will override the the run_impl() method and * implement the concrete value lookup behavior. In that method, subclasses * will use use dequeue_next_key() method to get the key that it will use to * collect the value(s), collect the appropriate value(s), and call the * store_value() method to save the value. The run_impl() method should * continue to dequeue and process values while the dequeue_next_key() method * returns true. * * The constructor for this class accepts a maximum wait time; this specifies * how long client code is willing to wait for a synchronous response (i.e., * how long the lookup() method will block waiting for the requested value). * If the async_key_value_source is able to collect the requested value * within that time period, then the lookup() method will return them. * * If the lookup() method is unable to collect the requested value within * the requested time period, then one of two things will happen. * *
    *
  1. If the client supplied a callback handler in the call to lookup(), then * that callback handler will be invoked by the async_key_value_source once * the value has been collected. Note that the callback handler will be * invoked in the context of the asynchronous thread associated with the * async_key_value_source.
  2. *
  3. If the client did not supply a handler, then the value will be stored, * and the next call to the lookup() method with the same key will return * the previously collected value. If lookup() is not called with the * specified ttl time, then this component will prune the stored value.
  4. *
* * @tparam key_type The type of the keys for which concrete subclasses will * query. This type must have a valid operator==(). * @tparam value_type The type of value that concrete subclasses will * receive from a query. This type must have a valid * operator=(). */ template class async_key_value_source { public: /** * If provided to the constructor as max_wait_ms, then lookup will * not wait for a response. */ const static uint64_t NO_WAIT_LOOKUP = 0; /** * A callback handler will take a key and a output reference to the * value. */ typedef std::function callback_handler; /** * Initialize this new async_key_value_source, which will block * synchronously for the given max_wait_ms for value collection. * * @param[in] max_wait_ms The maximum amount of time that client code * is willing to wait for lookup() to collect * a value before falling back to an async * return. * @param[in] ttl_ms The time, in milliseconds, that a cached * value will live before being considered * "too old" and being pruned. */ async_key_value_source(uint64_t max_wait_ms, uint64_t ttl_ms) noexcept; async_key_value_source(const async_key_value_source&) = delete; async_key_value_source(async_key_value_source&&) = delete; async_key_value_source& operator=(const async_key_value_source&) = delete; virtual ~async_key_value_source(); /** * Returns the maximum amount of time, in milliseconds, that a call to * lookup() will block synchronously before returning. */ uint64_t get_max_wait() const; /** * Returns the maximum amount of time, in milliseconds, that a cached * value will live before being pruned. */ uint64_t get_ttl() const; /** * Lookup value(s) based on the given key. This method will block * the caller for up the max_wait_ms time specified at construction * for the desired value(s) to be available. * * @param[in] key The key to the value for which the client * wishes to query. * @param[out] value If this method is able to fetch the desired * value within the max_wait_ms specified at * construction time, then this output parameter * will contain the collected value. The value * of this parameter is defined only if this method * returns true. * @param[in] handler If this method is unable to collect the requested * value(s) before the timeout, and if this parameter * is a valid, non-empty, function, then this class * will invoke the given handler from the async * thread immediately after the collected values * are available. If this handler is empty, then * this async_key_value_source will store the * values until either the next call to lookup() * or until its ttl expires, whichever comes first. * The handler is responsible for any thread-safety * guarantees. * * @returns true if this method was able to lookup and return the * value synchronously; false otherwise. */ bool lookup(const key_type& key, value_type& value, const callback_handler& handler = callback_handler()); /** * Lookup a value based on the specified key, after an initial delay. * This method behaves identically to `lookup()`, except that the request * is dispatched `delay` milliseconds after the call. * * @see lookup() for details */ bool lookup_delayed(const key_type& key, value_type& value, std::chrono::milliseconds delay, const callback_handler& handler = callback_handler()); /** * Determines if the async thread associated with this * async_key_value_source is running. * * Note: This API is for information only. Clients should * not use this to implement any sort of complex behavior. Such * use will lead to race conditions. For example, is_running() and * lookup() could potentially race, causing is_running() to return * false after lookup() has started the thread. * * @returns true if the async thread is running, false otherwise. */ bool is_running() const; /** * Return all results available so far * * All available results are moved from the internal map to the returned map * so subsequent `lookup()` and/or `get_complete_results()` calls won't * return them again. * * Sometimes there's no good place to call `lookup()` again * on the async data source -- e.g. the container detection engine * may never be called again for a particular container (if the only * process in that container never calls `execve()` or `chroot()` * or `clone()`). * * The best solution in that case is to supply a callback to be ran * from the async lookup, but that introduces thread safety issues * to the involved data. * * `get_complete_results()` allows batch processing of lookup results * in the main thread. * * @return a map of lookup key -> result */ std::unordered_map get_complete_results(); protected: /** * Stops the thread associated with this async_key_value_source, if * it is running; otherwise, does nothing. The only use for this is * in a destructor to ensure that the async thread stops when the * object is destroyed. */ void stop(); /** * Dequeues an entry from the request queue and returns it in the given * key. Concrete subclasses will call this method to get the next key * for which to collect values. * * @returns true if there was a key to dequeue, false otherwise. */ bool dequeue_next_key(key_type& key); /** * Get the (potentially partial) value for the given key. * * @param[in] key The key whose value is needed. * * @returns the value associated with the given key. */ value_type get_value(const key_type& key); /** * Stores a value for the given key. Concrete subclasses will call * this method from their run_impl() method to save (or otherwise * notify the client about) an available value. * * @param[in] key The key for which the client asked for the value. * @param[in] value The collected value. */ void store_value(const key_type& key, const value_type& value); /** * Concrete subclasses must override this method to perform the * asynchronous value lookup. The implementation should: * *
    *
  • Loop while dequeue_next_key() is true.
  • *
  • Get any existing value for that key using get_value()
  • *
  • Do whatever work is necessary to lookup the value associated * with that key.
  • *
  • Call store_value to store the updated value, and to * notify any client code waiting on that data.
  • *
*/ virtual void run_impl() = 0; /** * Determine the time to wait for the next request * * @return the absolute time until which run() may block while waiting * for an incoming request */ std::chrono::steady_clock::time_point get_deadline() const; private: /** * Holds information associated with a single lookup() request. */ struct lookup_request { lookup_request(): m_available(false), m_value(), m_available_condition(), m_callback(), m_start_time(std::chrono::steady_clock::now()) { } /** Is the value here available? */ bool m_available; /** The value for a key. */ value_type m_value; /** Block in lookup() waiting for a sync response. */ std::condition_variable m_available_condition; /** * A optional client-specified callback handler for async * response notification. */ callback_handler m_callback; /** The time at which this request was made. */ std::chrono::time_point m_start_time; }; typedef std::map value_map; /** * The entry point of the async thread, which blocks waiting for work * and dispatches work to run_impl(). */ void run(); /** * Remove any entries that are older than the time-to-live. */ void prune_stale_requests(); uint64_t m_max_wait_ms; uint64_t m_ttl_ms; std::thread m_thread; bool m_running; bool m_terminate; /** * Protects the state of instances of this class. This protected does * not extend to subclasses (i.e., this mutex should not be held when * dispatching to overridden methods). */ mutable std::mutex m_mutex; /** * Enables run() to block waiting for the m_request_queue to become * non-empty. */ std::condition_variable m_queue_not_empty_condition; using queue_item_t = std::pair, key_type>; std::priority_queue, std::greater> m_request_queue; std::set m_request_set; value_map m_value_map; }; } // end namespace sysdig #include "async_key_value_source.tpp" sysdig-0.26.4/userspace/async/async_key_value_source.tpp000066400000000000000000000206061352731327100235120ustar00rootroot00000000000000/* Copyright (C) 2019 Sysdig, Inc. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "logger.h" #include #include #include #include #include #include #include namespace sysdig { template async_key_value_source::async_key_value_source( const uint64_t max_wait_ms, const uint64_t ttl_ms) noexcept: m_max_wait_ms(max_wait_ms), m_ttl_ms(ttl_ms), m_thread(), m_running(false), m_terminate(false), m_mutex(), m_queue_not_empty_condition(), m_value_map() { } template async_key_value_source::~async_key_value_source() { try { stop(); } catch(...) { g_logger.log(std::string(__FUNCTION__) + ": Exception in destructor", sinsp_logger::SEV_ERROR); } } template uint64_t async_key_value_source::get_max_wait() const { return m_max_wait_ms; } template uint64_t async_key_value_source::get_ttl() const { return m_ttl_ms; } template void async_key_value_source::stop() { bool join_needed = false; { std::unique_lock guard(m_mutex); if(m_running) { m_terminate = true; join_needed = true; // The async thread might be waiting for new events // so wake it up m_queue_not_empty_condition.notify_one(); } } // Drop the mutex before join() if (join_needed) { m_thread.join(); // Remove any pointers from the thread to this object // (just to be safe) m_thread = std::thread(); } } template bool async_key_value_source::is_running() const { // Since this is for information only and it's ok to race, we // explicitly do not lock here. return m_running; } template void async_key_value_source::run() { m_running = true; while(!m_terminate) { { std::unique_lock guard(m_mutex); while(!m_terminate) { // Wait for something to show up on the queue auto deadline = get_deadline(); if (deadline == std::chrono::steady_clock::time_point::min()) { break; } else if (deadline == std::chrono::steady_clock::time_point::max()) { // https://stackoverflow.com/questions/39041450/stdcondition-variable-wait-until-surprising-behaviour m_queue_not_empty_condition.wait(guard); } else { m_queue_not_empty_condition.wait_until(guard, deadline); } } prune_stale_requests(); } if(!m_terminate) { run_impl(); } } m_running = false; } template bool async_key_value_source::lookup_delayed( const key_type& key, value_type& value, std::chrono::milliseconds delay, const callback_handler& handler) { std::unique_lock guard(m_mutex); if(!m_running && !m_thread.joinable()) { m_thread = std::thread(&async_key_value_source::run, this); } typename value_map::const_iterator itr = m_value_map.find(key); bool request_complete; if (itr == m_value_map.end()) { // Haven't made the request yet m_value_map[key].m_available = false; m_value_map[key].m_value = value; // Make request to API and let the async thread know about it if (std::find(m_request_set.begin(), m_request_set.end(), key) == m_request_set.end()) { auto start_time = std::chrono::steady_clock::now() + delay; m_request_queue.push(std::make_pair(start_time, key)); m_request_set.insert(key); m_queue_not_empty_condition.notify_one(); } request_complete = false; } else { request_complete = itr->second.m_available; } if(!request_complete && m_max_wait_ms > 0) { // // If the client code is willing to wait a short amount of time // to satisfy the request, then wait for the async thread to // pick up the newly-added request and execute it. If // processing that request takes too much time, then we'll // not be able to return the value information on this call, // and the async thread will continue handling the request so // that it'll be available on the next call. // m_value_map[key].m_available_condition.wait_for( guard, std::chrono::milliseconds(m_max_wait_ms)); itr = m_value_map.find(key); request_complete = (itr != m_value_map.end()) && itr->second.m_available; } if(request_complete) { value = itr->second.m_value; m_value_map.erase(key); } else { m_value_map[key].m_callback = handler; } return request_complete; } template bool async_key_value_source::lookup( const key_type& key, value_type& value, const callback_handler& handler) { return lookup_delayed(key, value, std::chrono::milliseconds::zero(), handler); } template bool async_key_value_source::dequeue_next_key(key_type& key) { std::lock_guard guard(m_mutex); bool key_found = false; if(!m_request_queue.empty()) { auto top_element = m_request_queue.top(); if(top_element.first < std::chrono::steady_clock::now()) { key_found = true; key = std::move(top_element.second); m_request_queue.pop(); m_request_set.erase(key); } } return key_found; } template value_type async_key_value_source::get_value( const key_type& key) { std::lock_guard guard(m_mutex); return m_value_map[key].m_value; } template void async_key_value_source::store_value( const key_type& key, const value_type& value) { std::lock_guard guard(m_mutex); if (m_value_map[key].m_callback) { m_value_map[key].m_callback(key, value); m_value_map.erase(key); } else { m_value_map[key].m_value = value; m_value_map[key].m_available = true; m_value_map[key].m_available_condition.notify_one(); } } /** * Prune any "old" outstanding requests. This method expects that the caller * is holding m_mutex. */ template void async_key_value_source::prune_stale_requests() { // Avoid both iterating over and modifying the map by saving a list // of keys to prune. std::vector keys_to_prune; for(auto i = m_value_map.begin(); !m_terminate && (i != m_value_map.end()); ++i) { const auto now = std::chrono::steady_clock::now(); const uint64_t age_ms = std::chrono::duration_cast( now - i->second.m_start_time).count(); if(age_ms > m_ttl_ms) { keys_to_prune.push_back(i->first); } } for(auto i = keys_to_prune.begin(); !m_terminate && (i != keys_to_prune.end()); ++i) { m_value_map.erase(*i); } } template std::unordered_map async_key_value_source::get_complete_results() { std::unordered_map results; std::lock_guard guard(m_mutex); for(const auto& it : m_value_map) { if(it.second.m_available) { results[it.first] = it.second.m_value; } } for(const auto& it : results) { m_value_map.erase(it.first); } return results; } // called with m_mutex held template std::chrono::steady_clock::time_point async_key_value_source::get_deadline() const { if (m_request_queue.empty()) { return std::chrono::steady_clock::time_point::max(); } auto next_request = m_request_queue.top(); if (next_request.first <= std::chrono::steady_clock::now()) { return std::chrono::steady_clock::time_point::min(); } return next_request.first; } } // end namespace sysdig sysdig-0.26.4/userspace/common/000077500000000000000000000000001352731327100163735ustar00rootroot00000000000000sysdig-0.26.4/userspace/common/sysdig_types.h000066400000000000000000000022561352731327100212770ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #ifdef _WIN32 #include #ifndef __cplusplus #define bool int #define false 0 #define true (!false) #define inline inline #endif /* __cplusplus */ #define snprintf _snprintf #else #define __STDC_FORMAT_MACROS #include #include #endif /* _WIN32 */ // // Import/typedef in userspace the kernel types // #if defined(__linux__) #include #else typedef uint64_t __u64; typedef int64_t __s64; typedef uint32_t __u32; typedef int32_t __s32; typedef uint16_t __u16; typedef int16_t __s16; typedef uint8_t __u8; typedef int8_t __s8; #endif sysdig-0.26.4/userspace/libscap/000077500000000000000000000000001352731327100165205ustar00rootroot00000000000000sysdig-0.26.4/userspace/libscap/CMakeLists.txt000066400000000000000000000040001352731327100212520ustar00rootroot00000000000000# # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # include_directories("${PROJECT_SOURCE_DIR}/common") include_directories("${ZLIB_INCLUDE}") if(CYGWIN) include_directories("${WIN_HAL_INCLUDE}") endif() list(APPEND targetfiles scap.c scap_event.c scap_fds.c scap_iflist.c scap_savefile.c scap_procs.c scap_userlist.c syscall_info_table.c ../../driver/dynamic_params_table.c ../../driver/event_table.c ../../driver/flags_table.c) if(CMAKE_SYSTEM_NAME MATCHES "Linux") list(APPEND targetfiles scap_bpf.c ../../driver/syscall_table.c ../../driver/fillers_table.c) include_directories(${PROJECT_BINARY_DIR}/driver/src) endif() if(CYGWIN) list(APPEND targetfiles windows_hal.c) endif() add_library(scap STATIC ${targetfiles}) if(USE_BUNDLED_ZLIB) add_dependencies(scap zlib) endif() if (CMAKE_SYSTEM_NAME MATCHES "SunOS") target_link_libraries(scap socket nsl) elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") target_link_libraries(scap elf rt) elseif (WIN32) target_link_libraries(scap Ws2_32.lib) elseif (CYGWIN) target_link_libraries(scap /lib/w32api/libpsapi.a ${WIN_HAL_LIB}/dragent_win_hal.lib) endif() target_link_libraries(scap "${ZLIB_LIB}") if(CMAKE_SYSTEM_NAME MATCHES "Linux") option(BUILD_LIBSCAP_EXAMPLES "Build libscap examples" ON) if (BUILD_LIBSCAP_EXAMPLES) add_subdirectory(examples/01-open) add_subdirectory(examples/02-validatebuffer) endif() include(FindMakedev) endif() sysdig-0.26.4/userspace/libscap/compat/000077500000000000000000000000001352731327100200035ustar00rootroot00000000000000sysdig-0.26.4/userspace/libscap/compat/bpf.h000066400000000000000000003226251352731327100207350ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. */ #ifndef _UAPI__LINUX_BPF_H__ #define _UAPI__LINUX_BPF_H__ #include #include "bpf_common.h" /* Extended instruction set based on top of classic BPF */ /* instruction classes */ #define BPF_ALU64 0x07 /* alu mode in double word width */ /* ld/ldx fields */ #define BPF_DW 0x18 /* double word (64-bit) */ #define BPF_XADD 0xc0 /* exclusive add */ /* alu/jmp fields */ #define BPF_MOV 0xb0 /* mov reg to reg */ #define BPF_ARSH 0xc0 /* sign extending arithmetic shift right */ /* change endianness of a register */ #define BPF_END 0xd0 /* flags for endianness conversion: */ #define BPF_TO_LE 0x00 /* convert to little-endian */ #define BPF_TO_BE 0x08 /* convert to big-endian */ #define BPF_FROM_LE BPF_TO_LE #define BPF_FROM_BE BPF_TO_BE /* jmp encodings */ #define BPF_JNE 0x50 /* jump != */ #define BPF_JLT 0xa0 /* LT is unsigned, '<' */ #define BPF_JLE 0xb0 /* LE is unsigned, '<=' */ #define BPF_JSGT 0x60 /* SGT is signed '>', GT in x86 */ #define BPF_JSGE 0x70 /* SGE is signed '>=', GE in x86 */ #define BPF_JSLT 0xc0 /* SLT is signed, '<' */ #define BPF_JSLE 0xd0 /* SLE is signed, '<=' */ #define BPF_CALL 0x80 /* function call */ #define BPF_EXIT 0x90 /* function return */ /* Register numbers */ enum { BPF_REG_0 = 0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5, BPF_REG_6, BPF_REG_7, BPF_REG_8, BPF_REG_9, BPF_REG_10, __MAX_BPF_REG, }; /* BPF has 10 general purpose 64-bit registers and stack frame. */ #define MAX_BPF_REG __MAX_BPF_REG struct bpf_insn { __u8 code; /* opcode */ __u8 dst_reg:4; /* dest register */ __u8 src_reg:4; /* source register */ __s16 off; /* signed offset */ __s32 imm; /* signed immediate constant */ }; /* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */ struct bpf_lpm_trie_key { __u32 prefixlen; /* up to 32 for AF_INET, 128 for AF_INET6 */ __u8 data[0]; /* Arbitrary size */ }; struct bpf_cgroup_storage_key { __u64 cgroup_inode_id; /* cgroup inode id */ __u32 attach_type; /* program attach type */ }; /* BPF syscall commands, see bpf(2) man-page for details. */ enum bpf_cmd { BPF_MAP_CREATE, BPF_MAP_LOOKUP_ELEM, BPF_MAP_UPDATE_ELEM, BPF_MAP_DELETE_ELEM, BPF_MAP_GET_NEXT_KEY, BPF_PROG_LOAD, BPF_OBJ_PIN, BPF_OBJ_GET, BPF_PROG_ATTACH, BPF_PROG_DETACH, BPF_PROG_TEST_RUN, BPF_PROG_GET_NEXT_ID, BPF_MAP_GET_NEXT_ID, BPF_PROG_GET_FD_BY_ID, BPF_MAP_GET_FD_BY_ID, BPF_OBJ_GET_INFO_BY_FD, BPF_PROG_QUERY, BPF_RAW_TRACEPOINT_OPEN, BPF_BTF_LOAD, BPF_BTF_GET_FD_BY_ID, BPF_TASK_FD_QUERY, }; enum bpf_map_type { BPF_MAP_TYPE_UNSPEC, BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PROG_ARRAY, BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_MAP_TYPE_PERCPU_HASH, BPF_MAP_TYPE_PERCPU_ARRAY, BPF_MAP_TYPE_STACK_TRACE, BPF_MAP_TYPE_CGROUP_ARRAY, BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, BPF_MAP_TYPE_LPM_TRIE, BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_DEVMAP, BPF_MAP_TYPE_SOCKMAP, BPF_MAP_TYPE_CPUMAP, BPF_MAP_TYPE_XSKMAP, BPF_MAP_TYPE_SOCKHASH, BPF_MAP_TYPE_CGROUP_STORAGE, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, }; enum bpf_prog_type { BPF_PROG_TYPE_UNSPEC, BPF_PROG_TYPE_SOCKET_FILTER, BPF_PROG_TYPE_KPROBE, BPF_PROG_TYPE_SCHED_CLS, BPF_PROG_TYPE_SCHED_ACT, BPF_PROG_TYPE_TRACEPOINT, BPF_PROG_TYPE_XDP, BPF_PROG_TYPE_PERF_EVENT, BPF_PROG_TYPE_CGROUP_SKB, BPF_PROG_TYPE_CGROUP_SOCK, BPF_PROG_TYPE_LWT_IN, BPF_PROG_TYPE_LWT_OUT, BPF_PROG_TYPE_LWT_XMIT, BPF_PROG_TYPE_SOCK_OPS, BPF_PROG_TYPE_SK_SKB, BPF_PROG_TYPE_CGROUP_DEVICE, BPF_PROG_TYPE_SK_MSG, BPF_PROG_TYPE_RAW_TRACEPOINT, BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_PROG_TYPE_LWT_SEG6LOCAL, BPF_PROG_TYPE_LIRC_MODE2, BPF_PROG_TYPE_SK_REUSEPORT, BPF_PROG_TYPE_FLOW_DISSECTOR, }; enum bpf_attach_type { BPF_CGROUP_INET_INGRESS, BPF_CGROUP_INET_EGRESS, BPF_CGROUP_INET_SOCK_CREATE, BPF_CGROUP_SOCK_OPS, BPF_SK_SKB_STREAM_PARSER, BPF_SK_SKB_STREAM_VERDICT, BPF_CGROUP_DEVICE, BPF_SK_MSG_VERDICT, BPF_CGROUP_INET4_BIND, BPF_CGROUP_INET6_BIND, BPF_CGROUP_INET4_CONNECT, BPF_CGROUP_INET6_CONNECT, BPF_CGROUP_INET4_POST_BIND, BPF_CGROUP_INET6_POST_BIND, BPF_CGROUP_UDP4_SENDMSG, BPF_CGROUP_UDP6_SENDMSG, BPF_LIRC_MODE2, BPF_FLOW_DISSECTOR, __MAX_BPF_ATTACH_TYPE }; #define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE /* cgroup-bpf attach flags used in BPF_PROG_ATTACH command * * NONE(default): No further bpf programs allowed in the subtree. * * BPF_F_ALLOW_OVERRIDE: If a sub-cgroup installs some bpf program, * the program in this cgroup yields to sub-cgroup program. * * BPF_F_ALLOW_MULTI: If a sub-cgroup installs some bpf program, * that cgroup program gets run in addition to the program in this cgroup. * * Only one program is allowed to be attached to a cgroup with * NONE or BPF_F_ALLOW_OVERRIDE flag. * Attaching another program on top of NONE or BPF_F_ALLOW_OVERRIDE will * release old program and attach the new one. Attach flags has to match. * * Multiple programs are allowed to be attached to a cgroup with * BPF_F_ALLOW_MULTI flag. They are executed in FIFO order * (those that were attached first, run first) * The programs of sub-cgroup are executed first, then programs of * this cgroup and then programs of parent cgroup. * When children program makes decision (like picking TCP CA or sock bind) * parent program has a chance to override it. * * A cgroup with MULTI or OVERRIDE flag allows any attach flags in sub-cgroups. * A cgroup with NONE doesn't allow any programs in sub-cgroups. * Ex1: * cgrp1 (MULTI progs A, B) -> * cgrp2 (OVERRIDE prog C) -> * cgrp3 (MULTI prog D) -> * cgrp4 (OVERRIDE prog E) -> * cgrp5 (NONE prog F) * the event in cgrp5 triggers execution of F,D,A,B in that order. * if prog F is detached, the execution is E,D,A,B * if prog F and D are detached, the execution is E,A,B * if prog F, E and D are detached, the execution is C,A,B * * All eligible programs are executed regardless of return code from * earlier programs. */ #define BPF_F_ALLOW_OVERRIDE (1U << 0) #define BPF_F_ALLOW_MULTI (1U << 1) /* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the * verifier will perform strict alignment checking as if the kernel * has been built with CONFIG_EFFICIENT_UNALIGNED_ACCESS not set, * and NET_IP_ALIGN defined to 2. */ #define BPF_F_STRICT_ALIGNMENT (1U << 0) /* when bpf_ldimm64->src_reg == BPF_PSEUDO_MAP_FD, bpf_ldimm64->imm == fd */ #define BPF_PSEUDO_MAP_FD 1 /* when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative * offset to another bpf function */ #define BPF_PSEUDO_CALL 1 /* flags for BPF_MAP_UPDATE_ELEM command */ #define BPF_ANY 0 /* create new element or update existing */ #define BPF_NOEXIST 1 /* create new element if it didn't exist */ #define BPF_EXIST 2 /* update existing element */ /* flags for BPF_MAP_CREATE command */ #define BPF_F_NO_PREALLOC (1U << 0) /* Instead of having one common LRU list in the * BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list * which can scale and perform better. * Note, the LRU nodes (including free nodes) cannot be moved * across different LRU lists. */ #define BPF_F_NO_COMMON_LRU (1U << 1) /* Specify numa node during map creation */ #define BPF_F_NUMA_NODE (1U << 2) /* flags for BPF_PROG_QUERY */ #define BPF_F_QUERY_EFFECTIVE (1U << 0) #define BPF_OBJ_NAME_LEN 16U /* Flags for accessing BPF object */ #define BPF_F_RDONLY (1U << 3) #define BPF_F_WRONLY (1U << 4) /* Flag for stack_map, store build_id+offset instead of pointer */ #define BPF_F_STACK_BUILD_ID (1U << 5) enum bpf_stack_build_id_status { /* user space need an empty entry to identify end of a trace */ BPF_STACK_BUILD_ID_EMPTY = 0, /* with valid build_id and offset */ BPF_STACK_BUILD_ID_VALID = 1, /* couldn't get build_id, fallback to ip */ BPF_STACK_BUILD_ID_IP = 2, }; #define BPF_BUILD_ID_SIZE 20 struct bpf_stack_build_id { __s32 status; unsigned char build_id[BPF_BUILD_ID_SIZE]; union { __u64 offset; __u64 ip; }; }; union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ __u32 key_size; /* size of key in bytes */ __u32 value_size; /* size of value in bytes */ __u32 max_entries; /* max number of entries in a map */ __u32 map_flags; /* BPF_MAP_CREATE related * flags defined above. */ __u32 inner_map_fd; /* fd pointing to the inner map */ __u32 numa_node; /* numa node (effective only if * BPF_F_NUMA_NODE is set). */ char map_name[BPF_OBJ_NAME_LEN]; __u32 map_ifindex; /* ifindex of netdev to create on */ __u32 btf_fd; /* fd pointing to a BTF type data */ __u32 btf_key_type_id; /* BTF type_id of the key */ __u32 btf_value_type_id; /* BTF type_id of the value */ }; struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ __u32 map_fd; __aligned_u64 key; union { __aligned_u64 value; __aligned_u64 next_key; }; __u64 flags; }; struct { /* anonymous struct used by BPF_PROG_LOAD command */ __u32 prog_type; /* one of enum bpf_prog_type */ __u32 insn_cnt; __aligned_u64 insns; __aligned_u64 license; __u32 log_level; /* verbosity level of verifier */ __u32 log_size; /* size of user buffer */ __aligned_u64 log_buf; /* user supplied buffer */ __u32 kern_version; /* checked when prog_type=kprobe */ __u32 prog_flags; char prog_name[BPF_OBJ_NAME_LEN]; __u32 prog_ifindex; /* ifindex of netdev to prep for */ /* For some prog types expected attach type must be known at * load time to verify attach type specific parts of prog * (context accesses, allowed helpers, etc). */ __u32 expected_attach_type; }; struct { /* anonymous struct used by BPF_OBJ_* commands */ __aligned_u64 pathname; __u32 bpf_fd; __u32 file_flags; }; struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */ __u32 target_fd; /* container object to attach to */ __u32 attach_bpf_fd; /* eBPF program to attach */ __u32 attach_type; __u32 attach_flags; }; struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */ __u32 prog_fd; __u32 retval; __u32 data_size_in; __u32 data_size_out; __aligned_u64 data_in; __aligned_u64 data_out; __u32 repeat; __u32 duration; } test; struct { /* anonymous struct used by BPF_*_GET_*_ID */ union { __u32 start_id; __u32 prog_id; __u32 map_id; __u32 btf_id; }; __u32 next_id; __u32 open_flags; }; struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */ __u32 bpf_fd; __u32 info_len; __aligned_u64 info; } info; struct { /* anonymous struct used by BPF_PROG_QUERY command */ __u32 target_fd; /* container object to query */ __u32 attach_type; __u32 query_flags; __u32 attach_flags; __aligned_u64 prog_ids; __u32 prog_cnt; } query; struct { __u64 name; __u32 prog_fd; } raw_tracepoint; struct { /* anonymous struct for BPF_BTF_LOAD */ __aligned_u64 btf; __aligned_u64 btf_log_buf; __u32 btf_size; __u32 btf_log_size; __u32 btf_log_level; }; struct { __u32 pid; /* input: pid */ __u32 fd; /* input: fd */ __u32 flags; /* input: flags */ __u32 buf_len; /* input/output: buf len */ __aligned_u64 buf; /* input/output: * tp_name for tracepoint * symbol for kprobe * filename for uprobe */ __u32 prog_id; /* output: prod_id */ __u32 fd_type; /* output: BPF_FD_TYPE_* */ __u64 probe_offset; /* output: probe_offset */ __u64 probe_addr; /* output: probe_addr */ } task_fd_query; } __attribute__((aligned(8))); /* The description below is an attempt at providing documentation to eBPF * developers about the multiple available eBPF helper functions. It can be * parsed and used to produce a manual page. The workflow is the following, * and requires the rst2man utility: * * $ ./scripts/bpf_helpers_doc.py \ * --filename include/uapi/linux/bpf.h > /tmp/bpf-helpers.rst * $ rst2man /tmp/bpf-helpers.rst > /tmp/bpf-helpers.7 * $ man /tmp/bpf-helpers.7 * * Note that in order to produce this external documentation, some RST * formatting is used in the descriptions to get "bold" and "italics" in * manual pages. Also note that the few trailing white spaces are * intentional, removing them would break paragraphs for rst2man. * * Start of BPF helper function descriptions: * * void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) * Description * Perform a lookup in *map* for an entry associated to *key*. * Return * Map value associated to *key*, or **NULL** if no entry was * found. * * int bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags) * Description * Add or update the value of the entry associated to *key* in * *map* with *value*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * Flag value **BPF_NOEXIST** cannot be used for maps of types * **BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY** (all * elements always exist), the helper would return an error. * Return * 0 on success, or a negative error in case of failure. * * int bpf_map_delete_elem(struct bpf_map *map, const void *key) * Description * Delete entry with *key* from *map*. * Return * 0 on success, or a negative error in case of failure. * * int bpf_probe_read(void *dst, u32 size, const void *src) * Description * For tracing programs, safely attempt to read *size* bytes from * address *src* and store the data in *dst*. * Return * 0 on success, or a negative error in case of failure. * * u64 bpf_ktime_get_ns(void) * Description * Return the time elapsed since system boot, in nanoseconds. * Return * Current *ktime*. * * int bpf_trace_printk(const char *fmt, u32 fmt_size, ...) * Description * This helper is a "printk()-like" facility for debugging. It * prints a message defined by format *fmt* (of size *fmt_size*) * to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if * available. It can take up to three additional **u64** * arguments (as an eBPF helpers, the total number of arguments is * limited to five). * * Each time the helper is called, it appends a line to the trace. * The format of the trace is customizable, and the exact output * one will get depends on the options set in * *\/sys/kernel/debug/tracing/trace_options* (see also the * *README* file under the same directory). However, it usually * defaults to something like: * * :: * * telnet-470 [001] .N.. 419421.045894: 0x00000001: * * In the above: * * * ``telnet`` is the name of the current task. * * ``470`` is the PID of the current task. * * ``001`` is the CPU number on which the task is * running. * * In ``.N..``, each character refers to a set of * options (whether irqs are enabled, scheduling * options, whether hard/softirqs are running, level of * preempt_disabled respectively). **N** means that * **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED** * are set. * * ``419421.045894`` is a timestamp. * * ``0x00000001`` is a fake value used by BPF for the * instruction pointer register. * * ```` is the message formatted with * *fmt*. * * The conversion specifiers supported by *fmt* are similar, but * more limited than for printk(). They are **%d**, **%i**, * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**, * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size * of field, padding with zeroes, etc.) is available, and the * helper will return **-EINVAL** (but print nothing) if it * encounters an unknown specifier. * * Also, note that **bpf_trace_printk**\ () is slow, and should * only be used for debugging purposes. For this reason, a notice * bloc (spanning several lines) is printed to kernel logs and * states that the helper should not be used "for production use" * the first time this helper is used (or more precisely, when * **trace_printk**\ () buffers are allocated). For passing values * to user space, perf events should be preferred. * Return * The number of bytes written to the buffer, or a negative error * in case of failure. * * u32 bpf_get_prandom_u32(void) * Description * Get a pseudo-random number. * * From a security point of view, this helper uses its own * pseudo-random internal state, and cannot be used to infer the * seed of other random functions in the kernel. However, it is * essential to note that the generator used by the helper is not * cryptographically secure. * Return * A random 32-bit unsigned value. * * u32 bpf_get_smp_processor_id(void) * Description * Get the SMP (symmetric multiprocessing) processor id. Note that * all programs run with preemption disabled, which means that the * SMP processor id is stable during all the execution of the * program. * Return * The SMP id of the processor running the program. * * int bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len, u64 flags) * Description * Store *len* bytes from address *from* into the packet * associated to *skb*, at *offset*. *flags* are a combination of * **BPF_F_RECOMPUTE_CSUM** (automatically recompute the * checksum for the packet after storing the bytes) and * **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\ * **->swhash** and *skb*\ **->l4hash** to 0). * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_l3_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 size) * Description * Recompute the layer 3 (e.g. IP) checksum for the packet * associated to *skb*. Computation is incremental, so the helper * must know the former value of the header field that was * modified (*from*), the new value of this field (*to*), and the * number of bytes (2 or 4) for this field, stored in *size*. * Alternatively, it is possible to store the difference between * the previous and the new values of the header field in *to*, by * setting *from* and *size* to 0. For both methods, *offset* * indicates the location of the IP checksum within the packet. * * This helper works in combination with **bpf_csum_diff**\ (), * which does not update the checksum in-place, but offers more * flexibility and can handle sizes larger than 2 or 4 for the * checksum to update. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_l4_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 flags) * Description * Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the * packet associated to *skb*. Computation is incremental, so the * helper must know the former value of the header field that was * modified (*from*), the new value of this field (*to*), and the * number of bytes (2 or 4) for this field, stored on the lowest * four bits of *flags*. Alternatively, it is possible to store * the difference between the previous and the new values of the * header field in *to*, by setting *from* and the four lowest * bits of *flags* to 0. For both methods, *offset* indicates the * location of the IP checksum within the packet. In addition to * the size of the field, *flags* can be added (bitwise OR) actual * flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and * for updates resulting in a null checksum the value is set to * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates * the checksum is to be computed against a pseudo-header. * * This helper works in combination with **bpf_csum_diff**\ (), * which does not update the checksum in-place, but offers more * flexibility and can handle sizes larger than 2 or 4 for the * checksum to update. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_tail_call(void *ctx, struct bpf_map *prog_array_map, u32 index) * Description * This special helper is used to trigger a "tail call", or in * other words, to jump into another eBPF program. The same stack * frame is used (but values on stack and in registers for the * caller are not accessible to the callee). This mechanism allows * for program chaining, either for raising the maximum number of * available eBPF instructions, or to execute given programs in * conditional blocks. For security reasons, there is an upper * limit to the number of successive tail calls that can be * performed. * * Upon call of this helper, the program attempts to jump into a * program referenced at index *index* in *prog_array_map*, a * special map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes * *ctx*, a pointer to the context. * * If the call succeeds, the kernel immediately runs the first * instruction of the new program. This is not a function call, * and it never returns to the previous program. If the call * fails, then the helper has no effect, and the caller continues * to run its subsequent instructions. A call can fail if the * destination program for the jump does not exist (i.e. *index* * is superior to the number of entries in *prog_array_map*), or * if the maximum number of tail calls has been reached for this * chain of programs. This limit is defined in the kernel by the * macro **MAX_TAIL_CALL_CNT** (not accessible to user space), * which is currently set to 32. * Return * 0 on success, or a negative error in case of failure. * * int bpf_clone_redirect(struct sk_buff *skb, u32 ifindex, u64 flags) * Description * Clone and redirect the packet associated to *skb* to another * net device of index *ifindex*. Both ingress and egress * interfaces can be used for redirection. The **BPF_F_INGRESS** * value in *flags* is used to make the distinction (ingress path * is selected if the flag is present, egress path otherwise). * This is the only flag supported for now. * * In comparison with **bpf_redirect**\ () helper, * **bpf_clone_redirect**\ () has the associated cost of * duplicating the packet buffer, but this can be executed out of * the eBPF program. Conversely, **bpf_redirect**\ () is more * efficient, but it is handled through an action code where the * redirection happens only after the eBPF program has returned. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * u64 bpf_get_current_pid_tgid(void) * Return * A 64-bit integer containing the current tgid and pid, and * created as such: * *current_task*\ **->tgid << 32 \|** * *current_task*\ **->pid**. * * u64 bpf_get_current_uid_gid(void) * Return * A 64-bit integer containing the current GID and UID, and * created as such: *current_gid* **<< 32 \|** *current_uid*. * * int bpf_get_current_comm(char *buf, u32 size_of_buf) * Description * Copy the **comm** attribute of the current task into *buf* of * *size_of_buf*. The **comm** attribute contains the name of * the executable (excluding the path) for the current task. The * *size_of_buf* must be strictly positive. On success, the * helper makes sure that the *buf* is NUL-terminated. On failure, * it is filled with zeroes. * Return * 0 on success, or a negative error in case of failure. * * u32 bpf_get_cgroup_classid(struct sk_buff *skb) * Description * Retrieve the classid for the current task, i.e. for the net_cls * cgroup to which *skb* belongs. * * This helper can be used on TC egress path, but not on ingress. * * The net_cls cgroup provides an interface to tag network packets * based on a user-provided identifier for all traffic coming from * the tasks belonging to the related cgroup. See also the related * kernel documentation, available from the Linux sources in file * *Documentation/cgroup-v1/net_cls.txt*. * * The Linux kernel has two versions for cgroups: there are * cgroups v1 and cgroups v2. Both are available to users, who can * use a mixture of them, but note that the net_cls cgroup is for * cgroup v1 only. This makes it incompatible with BPF programs * run on cgroups, which is a cgroup-v2-only feature (a socket can * only hold data for one version of cgroups at a time). * * This helper is only available is the kernel was compiled with * the **CONFIG_CGROUP_NET_CLASSID** configuration option set to * "**y**" or to "**m**". * Return * The classid, or 0 for the default unconfigured classid. * * int bpf_skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci) * Description * Push a *vlan_tci* (VLAN tag control information) of protocol * *vlan_proto* to the packet associated to *skb*, then update * the checksum. Note that if *vlan_proto* is different from * **ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to * be **ETH_P_8021Q**. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_vlan_pop(struct sk_buff *skb) * Description * Pop a VLAN header from the packet associated to *skb*. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_get_tunnel_key(struct sk_buff *skb, struct bpf_tunnel_key *key, u32 size, u64 flags) * Description * Get tunnel metadata. This helper takes a pointer *key* to an * empty **struct bpf_tunnel_key** of **size**, that will be * filled with tunnel metadata for the packet associated to *skb*. * The *flags* can be set to **BPF_F_TUNINFO_IPV6**, which * indicates that the tunnel is based on IPv6 protocol instead of * IPv4. * * The **struct bpf_tunnel_key** is an object that generalizes the * principal parameters used by various tunneling protocols into a * single struct. This way, it can be used to easily make a * decision based on the contents of the encapsulation header, * "summarized" in this struct. In particular, it holds the IP * address of the remote end (IPv4 or IPv6, depending on the case) * in *key*\ **->remote_ipv4** or *key*\ **->remote_ipv6**. Also, * this struct exposes the *key*\ **->tunnel_id**, which is * generally mapped to a VNI (Virtual Network Identifier), making * it programmable together with the **bpf_skb_set_tunnel_key**\ * () helper. * * Let's imagine that the following code is part of a program * attached to the TC ingress interface, on one end of a GRE * tunnel, and is supposed to filter out all messages coming from * remote ends with IPv4 address other than 10.0.0.1: * * :: * * int ret; * struct bpf_tunnel_key key = {}; * * ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0); * if (ret < 0) * return TC_ACT_SHOT; // drop packet * * if (key.remote_ipv4 != 0x0a000001) * return TC_ACT_SHOT; // drop packet * * return TC_ACT_OK; // accept packet * * This interface can also be used with all encapsulation devices * that can operate in "collect metadata" mode: instead of having * one network device per specific configuration, the "collect * metadata" mode only requires a single device where the * configuration can be extracted from this helper. * * This can be used together with various tunnels such as VXLan, * Geneve, GRE or IP in IP (IPIP). * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_set_tunnel_key(struct sk_buff *skb, struct bpf_tunnel_key *key, u32 size, u64 flags) * Description * Populate tunnel metadata for packet associated to *skb.* The * tunnel metadata is set to the contents of *key*, of *size*. The * *flags* can be set to a combination of the following values: * * **BPF_F_TUNINFO_IPV6** * Indicate that the tunnel is based on IPv6 protocol * instead of IPv4. * **BPF_F_ZERO_CSUM_TX** * For IPv4 packets, add a flag to tunnel metadata * indicating that checksum computation should be skipped * and checksum set to zeroes. * **BPF_F_DONT_FRAGMENT** * Add a flag to tunnel metadata indicating that the * packet should not be fragmented. * **BPF_F_SEQ_NUMBER** * Add a flag to tunnel metadata indicating that a * sequence number should be added to tunnel header before * sending the packet. This flag was added for GRE * encapsulation, but might be used with other protocols * as well in the future. * * Here is a typical usage on the transmit path: * * :: * * struct bpf_tunnel_key key; * populate key ... * bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0); * bpf_clone_redirect(skb, vxlan_dev_ifindex, 0); * * See also the description of the **bpf_skb_get_tunnel_key**\ () * helper for additional information. * Return * 0 on success, or a negative error in case of failure. * * u64 bpf_perf_event_read(struct bpf_map *map, u64 flags) * Description * Read the value of a perf event counter. This helper relies on a * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of * the perf event counter is selected when *map* is updated with * perf event file descriptors. The *map* is an array whose size * is the number of available CPUs, and each cell contains a value * relative to one CPU. The value to retrieve is indicated by * *flags*, that contains the index of the CPU to look up, masked * with **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to * **BPF_F_CURRENT_CPU** to indicate that the value for the * current CPU should be retrieved. * * Note that before Linux 4.13, only hardware perf event can be * retrieved. * * Also, be aware that the newer helper * **bpf_perf_event_read_value**\ () is recommended over * **bpf_perf_event_read**\ () in general. The latter has some ABI * quirks where error and counter value are used as a return code * (which is wrong to do since ranges may overlap). This issue is * fixed with **bpf_perf_event_read_value**\ (), which at the same * time provides more features over the **bpf_perf_event_read**\ * () interface. Please refer to the description of * **bpf_perf_event_read_value**\ () for details. * Return * The value of the perf event counter read from the map, or a * negative error code in case of failure. * * int bpf_redirect(u32 ifindex, u64 flags) * Description * Redirect the packet to another net device of index *ifindex*. * This helper is somewhat similar to **bpf_clone_redirect**\ * (), except that the packet is not cloned, which provides * increased performance. * * Except for XDP, both ingress and egress interfaces can be used * for redirection. The **BPF_F_INGRESS** value in *flags* is used * to make the distinction (ingress path is selected if the flag * is present, egress path otherwise). Currently, XDP only * supports redirection to the egress interface, and accepts no * flag at all. * * The same effect can be attained with the more generic * **bpf_redirect_map**\ (), which requires specific maps to be * used but offers better performance. * Return * For XDP, the helper returns **XDP_REDIRECT** on success or * **XDP_ABORTED** on error. For other program types, the values * are **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on * error. * * u32 bpf_get_route_realm(struct sk_buff *skb) * Description * Retrieve the realm or the route, that is to say the * **tclassid** field of the destination for the *skb*. The * indentifier retrieved is a user-provided tag, similar to the * one used with the net_cls cgroup (see description for * **bpf_get_cgroup_classid**\ () helper), but here this tag is * held by a route (a destination entry), not by a task. * * Retrieving this identifier works with the clsact TC egress hook * (see also **tc-bpf(8)**), or alternatively on conventional * classful egress qdiscs, but not on TC ingress path. In case of * clsact TC egress hook, this has the advantage that, internally, * the destination entry has not been dropped yet in the transmit * path. Therefore, the destination entry does not need to be * artificially held via **netif_keep_dst**\ () for a classful * qdisc until the *skb* is freed. * * This helper is available only if the kernel was compiled with * **CONFIG_IP_ROUTE_CLASSID** configuration option. * Return * The realm of the route for the packet associated to *skb*, or 0 * if none was found. * * int bpf_perf_event_output(struct pt_reg *ctx, struct bpf_map *map, u64 flags, void *data, u64 size) * Description * Write raw *data* blob into a special BPF perf event held by * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf * event must have the following attributes: **PERF_SAMPLE_RAW** * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. * * The *flags* are used to indicate the index in *map* for which * the value must be put, masked with **BPF_F_INDEX_MASK**. * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** * to indicate that the index of the current CPU core should be * used. * * The value to write, of *size*, is passed through eBPF stack and * pointed by *data*. * * The context of the program *ctx* needs also be passed to the * helper. * * On user space, a program willing to read the values needs to * call **perf_event_open**\ () on the perf event (either for * one or for all CPUs) and to store the file descriptor into the * *map*. This must be done before the eBPF program can send data * into it. An example is available in file * *samples/bpf/trace_output_user.c* in the Linux kernel source * tree (the eBPF program counterpart is in * *samples/bpf/trace_output_kern.c*). * * **bpf_perf_event_output**\ () achieves better performance * than **bpf_trace_printk**\ () for sharing data with user * space, and is much better suitable for streaming data from eBPF * programs. * * Note that this helper is not restricted to tracing use cases * and can be used with programs attached to TC or XDP as well, * where it allows for passing data to user space listeners. Data * can be: * * * Only custom structs, * * Only the packet payload, or * * A combination of both. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_load_bytes(const struct sk_buff *skb, u32 offset, void *to, u32 len) * Description * This helper was provided as an easy way to load data from a * packet. It can be used to load *len* bytes from *offset* from * the packet associated to *skb*, into the buffer pointed by * *to*. * * Since Linux 4.7, usage of this helper has mostly been replaced * by "direct packet access", enabling packet data to be * manipulated with *skb*\ **->data** and *skb*\ **->data_end** * pointing respectively to the first byte of packet data and to * the byte after the last byte of packet data. However, it * remains useful if one wishes to read large quantities of data * at once from a packet into the eBPF stack. * Return * 0 on success, or a negative error in case of failure. * * int bpf_get_stackid(struct pt_reg *ctx, struct bpf_map *map, u64 flags) * Description * Walk a user or a kernel stack and return its id. To achieve * this, the helper needs *ctx*, which is a pointer to the context * on which the tracing program is executed, and a pointer to a * *map* of type **BPF_MAP_TYPE_STACK_TRACE**. * * The last argument, *flags*, holds the number of stack frames to * skip (from 0 to 255), masked with * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set * a combination of the following flags: * * **BPF_F_USER_STACK** * Collect a user space stack instead of a kernel stack. * **BPF_F_FAST_STACK_CMP** * Compare stacks by hash only. * **BPF_F_REUSE_STACKID** * If two different stacks hash into the same *stackid*, * discard the old one. * * The stack id retrieved is a 32 bit long integer handle which * can be further combined with other data (including other stack * ids) and used as a key into maps. This can be useful for * generating a variety of graphs (such as flame graphs or off-cpu * graphs). * * For walking a stack, this helper is an improvement over * **bpf_probe_read**\ (), which can be used with unrolled loops * but is not efficient and consumes a lot of eBPF instructions. * Instead, **bpf_get_stackid**\ () can collect up to * **PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that * this limit can be controlled with the **sysctl** program, and * that it should be manually increased in order to profile long * user stacks (such as stacks for Java programs). To do so, use: * * :: * * # sysctl kernel.perf_event_max_stack= * Return * The positive or null stack id on success, or a negative error * in case of failure. * * s64 bpf_csum_diff(__be32 *from, u32 from_size, __be32 *to, u32 to_size, __wsum seed) * Description * Compute a checksum difference, from the raw buffer pointed by * *from*, of length *from_size* (that must be a multiple of 4), * towards the raw buffer pointed by *to*, of size *to_size* * (same remark). An optional *seed* can be added to the value * (this can be cascaded, the seed may come from a previous call * to the helper). * * This is flexible enough to be used in several ways: * * * With *from_size* == 0, *to_size* > 0 and *seed* set to * checksum, it can be used when pushing new data. * * With *from_size* > 0, *to_size* == 0 and *seed* set to * checksum, it can be used when removing data from a packet. * * With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it * can be used to compute a diff. Note that *from_size* and * *to_size* do not need to be equal. * * This helper can be used in combination with * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ (), to * which one can feed in the difference computed with * **bpf_csum_diff**\ (). * Return * The checksum result, or a negative error code in case of * failure. * * int bpf_skb_get_tunnel_opt(struct sk_buff *skb, u8 *opt, u32 size) * Description * Retrieve tunnel options metadata for the packet associated to * *skb*, and store the raw tunnel option data to the buffer *opt* * of *size*. * * This helper can be used with encapsulation devices that can * operate in "collect metadata" mode (please refer to the related * note in the description of **bpf_skb_get_tunnel_key**\ () for * more details). A particular example where this can be used is * in combination with the Geneve encapsulation protocol, where it * allows for pushing (with **bpf_skb_get_tunnel_opt**\ () helper) * and retrieving arbitrary TLVs (Type-Length-Value headers) from * the eBPF program. This allows for full customization of these * headers. * Return * The size of the option data retrieved. * * int bpf_skb_set_tunnel_opt(struct sk_buff *skb, u8 *opt, u32 size) * Description * Set tunnel options metadata for the packet associated to *skb* * to the option data contained in the raw buffer *opt* of *size*. * * See also the description of the **bpf_skb_get_tunnel_opt**\ () * helper for additional information. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_change_proto(struct sk_buff *skb, __be16 proto, u64 flags) * Description * Change the protocol of the *skb* to *proto*. Currently * supported are transition from IPv4 to IPv6, and from IPv6 to * IPv4. The helper takes care of the groundwork for the * transition, including resizing the socket buffer. The eBPF * program is expected to fill the new headers, if any, via * **skb_store_bytes**\ () and to recompute the checksums with * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ * (). The main case for this helper is to perform NAT64 * operations out of an eBPF program. * * Internally, the GSO type is marked as dodgy so that headers are * checked and segments are recalculated by the GSO/GRO engine. * The size for GSO target is adapted as well. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_change_type(struct sk_buff *skb, u32 type) * Description * Change the packet type for the packet associated to *skb*. This * comes down to setting *skb*\ **->pkt_type** to *type*, except * the eBPF program does not have a write access to *skb*\ * **->pkt_type** beside this helper. Using a helper here allows * for graceful handling of errors. * * The major use case is to change incoming *skb*s to * **PACKET_HOST** in a programmatic way instead of having to * recirculate via **redirect**\ (..., **BPF_F_INGRESS**), for * example. * * Note that *type* only allows certain values. At this time, they * are: * * **PACKET_HOST** * Packet is for us. * **PACKET_BROADCAST** * Send packet to all. * **PACKET_MULTICAST** * Send packet to group. * **PACKET_OTHERHOST** * Send packet to someone else. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_under_cgroup(struct sk_buff *skb, struct bpf_map *map, u32 index) * Description * Check whether *skb* is a descendant of the cgroup2 held by * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. * Return * The return value depends on the result of the test, and can be: * * * 0, if the *skb* failed the cgroup2 descendant test. * * 1, if the *skb* succeeded the cgroup2 descendant test. * * A negative error code, if an error occurred. * * u32 bpf_get_hash_recalc(struct sk_buff *skb) * Description * Retrieve the hash of the packet, *skb*\ **->hash**. If it is * not set, in particular if the hash was cleared due to mangling, * recompute this hash. Later accesses to the hash can be done * directly with *skb*\ **->hash**. * * Calling **bpf_set_hash_invalid**\ (), changing a packet * prototype with **bpf_skb_change_proto**\ (), or calling * **bpf_skb_store_bytes**\ () with the * **BPF_F_INVALIDATE_HASH** are actions susceptible to clear * the hash and to trigger a new computation for the next call to * **bpf_get_hash_recalc**\ (). * Return * The 32-bit hash. * * u64 bpf_get_current_task(void) * Return * A pointer to the current task struct. * * int bpf_probe_write_user(void *dst, const void *src, u32 len) * Description * Attempt in a safe way to write *len* bytes from the buffer * *src* to *dst* in memory. It only works for threads that are in * user context, and *dst* must be a valid user space address. * * This helper should not be used to implement any kind of * security mechanism because of TOC-TOU attacks, but rather to * debug, divert, and manipulate execution of semi-cooperative * processes. * * Keep in mind that this feature is meant for experiments, and it * has a risk of crashing the system and running programs. * Therefore, when an eBPF program using this helper is attached, * a warning including PID and process name is printed to kernel * logs. * Return * 0 on success, or a negative error in case of failure. * * int bpf_current_task_under_cgroup(struct bpf_map *map, u32 index) * Description * Check whether the probe is being run is the context of a given * subset of the cgroup2 hierarchy. The cgroup2 to test is held by * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. * Return * The return value depends on the result of the test, and can be: * * * 0, if the *skb* task belongs to the cgroup2. * * 1, if the *skb* task does not belong to the cgroup2. * * A negative error code, if an error occurred. * * int bpf_skb_change_tail(struct sk_buff *skb, u32 len, u64 flags) * Description * Resize (trim or grow) the packet associated to *skb* to the * new *len*. The *flags* are reserved for future usage, and must * be left at zero. * * The basic idea is that the helper performs the needed work to * change the size of the packet, then the eBPF program rewrites * the rest via helpers like **bpf_skb_store_bytes**\ (), * **bpf_l3_csum_replace**\ (), **bpf_l3_csum_replace**\ () * and others. This helper is a slow path utility intended for * replies with control messages. And because it is targeted for * slow path, the helper itself can afford to be slow: it * implicitly linearizes, unclones and drops offloads from the * *skb*. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_pull_data(struct sk_buff *skb, u32 len) * Description * Pull in non-linear data in case the *skb* is non-linear and not * all of *len* are part of the linear section. Make *len* bytes * from *skb* readable and writable. If a zero value is passed for * *len*, then the whole length of the *skb* is pulled. * * This helper is only needed for reading and writing with direct * packet access. * * For direct packet access, testing that offsets to access * are within packet boundaries (test on *skb*\ **->data_end**) is * susceptible to fail if offsets are invalid, or if the requested * data is in non-linear parts of the *skb*. On failure the * program can just bail out, or in the case of a non-linear * buffer, use a helper to make the data available. The * **bpf_skb_load_bytes**\ () helper is a first solution to access * the data. Another one consists in using **bpf_skb_pull_data** * to pull in once the non-linear parts, then retesting and * eventually access the data. * * At the same time, this also makes sure the *skb* is uncloned, * which is a necessary condition for direct write. As this needs * to be an invariant for the write part only, the verifier * detects writes and adds a prologue that is calling * **bpf_skb_pull_data()** to effectively unclone the *skb* from * the very beginning in case it is indeed cloned. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * s64 bpf_csum_update(struct sk_buff *skb, __wsum csum) * Description * Add the checksum *csum* into *skb*\ **->csum** in case the * driver has supplied a checksum for the entire packet into that * field. Return an error otherwise. This helper is intended to be * used in combination with **bpf_csum_diff**\ (), in particular * when the checksum needs to be updated after data has been * written into the packet through direct packet access. * Return * The checksum on success, or a negative error code in case of * failure. * * void bpf_set_hash_invalid(struct sk_buff *skb) * Description * Invalidate the current *skb*\ **->hash**. It can be used after * mangling on headers through direct packet access, in order to * indicate that the hash is outdated and to trigger a * recalculation the next time the kernel tries to access this * hash or when the **bpf_get_hash_recalc**\ () helper is called. * * int bpf_get_numa_node_id(void) * Description * Return the id of the current NUMA node. The primary use case * for this helper is the selection of sockets for the local NUMA * node, when the program is attached to sockets using the * **SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**), * but the helper is also available to other eBPF program types, * similarly to **bpf_get_smp_processor_id**\ (). * Return * The id of current NUMA node. * * int bpf_skb_change_head(struct sk_buff *skb, u32 len, u64 flags) * Description * Grows headroom of packet associated to *skb* and adjusts the * offset of the MAC header accordingly, adding *len* bytes of * space. It automatically extends and reallocates memory as * required. * * This helper can be used on a layer 3 *skb* to push a MAC header * for redirection into a layer 2 device. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_xdp_adjust_head(struct xdp_buff *xdp_md, int delta) * Description * Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that * it is possible to use a negative value for *delta*. This helper * can be used to prepare the packet for pushing or popping * headers. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr) * Description * Copy a NUL terminated string from an unsafe address * *unsafe_ptr* to *dst*. The *size* should include the * terminating NUL byte. In case the string length is smaller than * *size*, the target is not padded with further NUL bytes. If the * string length is larger than *size*, just *size*-1 bytes are * copied and the last byte is set to NUL. * * On success, the length of the copied string is returned. This * makes this helper useful in tracing programs for reading * strings, and more importantly to get its length at runtime. See * the following snippet: * * :: * * SEC("kprobe/sys_open") * void bpf_sys_open(struct pt_regs *ctx) * { * char buf[PATHLEN]; // PATHLEN is defined to 256 * int res = bpf_probe_read_str(buf, sizeof(buf), * ctx->di); * * // Consume buf, for example push it to * // userspace via bpf_perf_event_output(); we * // can use res (the string length) as event * // size, after checking its boundaries. * } * * In comparison, using **bpf_probe_read()** helper here instead * to read the string would require to estimate the length at * compile time, and would often result in copying more memory * than necessary. * * Another useful use case is when parsing individual process * arguments or individual environment variables navigating * *current*\ **->mm->arg_start** and *current*\ * **->mm->env_start**: using this helper and the return value, * one can quickly iterate at the right offset of the memory area. * Return * On success, the strictly positive length of the string, * including the trailing NUL character. On error, a negative * value. * * u64 bpf_get_socket_cookie(struct sk_buff *skb) * Description * If the **struct sk_buff** pointed by *skb* has a known socket, * retrieve the cookie (generated by the kernel) of this socket. * If no cookie has been set yet, generate a new cookie. Once * generated, the socket cookie remains stable for the life of the * socket. This helper can be useful for monitoring per socket * networking traffic statistics as it provides a unique socket * identifier per namespace. * Return * A 8-byte long non-decreasing number on success, or 0 if the * socket field is missing inside *skb*. * * u64 bpf_get_socket_cookie(struct bpf_sock_addr *ctx) * Description * Equivalent to bpf_get_socket_cookie() helper that accepts * *skb*, but gets socket from **struct bpf_sock_addr** contex. * Return * A 8-byte long non-decreasing number. * * u64 bpf_get_socket_cookie(struct bpf_sock_ops *ctx) * Description * Equivalent to bpf_get_socket_cookie() helper that accepts * *skb*, but gets socket from **struct bpf_sock_ops** contex. * Return * A 8-byte long non-decreasing number. * * u32 bpf_get_socket_uid(struct sk_buff *skb) * Return * The owner UID of the socket associated to *skb*. If the socket * is **NULL**, or if it is not a full socket (i.e. if it is a * time-wait or a request socket instead), **overflowuid** value * is returned (note that **overflowuid** might also be the actual * UID value for the socket). * * u32 bpf_set_hash(struct sk_buff *skb, u32 hash) * Description * Set the full hash for *skb* (set the field *skb*\ **->hash**) * to value *hash*. * Return * 0 * * int bpf_setsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, char *optval, int optlen) * Description * Emulate a call to **setsockopt()** on the socket associated to * *bpf_socket*, which must be a full socket. The *level* at * which the option resides and the name *optname* of the option * must be specified, see **setsockopt(2)** for more information. * The option value of length *optlen* is pointed by *optval*. * * This helper actually implements a subset of **setsockopt()**. * It supports the following *level*\ s: * * * **SOL_SOCKET**, which supports the following *optname*\ s: * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**. * * **IPPROTO_TCP**, which supports the following *optname*\ s: * **TCP_CONGESTION**, **TCP_BPF_IW**, * **TCP_BPF_SNDCWND_CLAMP**. * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_adjust_room(struct sk_buff *skb, u32 len_diff, u32 mode, u64 flags) * Description * Grow or shrink the room for data in the packet associated to * *skb* by *len_diff*, and according to the selected *mode*. * * There is a single supported mode at this time: * * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer * (room space is added or removed below the layer 3 header). * * All values for *flags* are reserved for future usage, and must * be left at zero. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags) * Description * Redirect the packet to the endpoint referenced by *map* at * index *key*. Depending on its type, this *map* can contain * references to net devices (for forwarding packets through other * ports), or to CPUs (for redirecting XDP frames to another CPU; * but this is only implemented for native XDP (with driver * support) as of this writing). * * All values for *flags* are reserved for future usage, and must * be left at zero. * * When used to redirect packets to net devices, this helper * provides a high performance increase over **bpf_redirect**\ (). * This is due to various implementation details of the underlying * mechanisms, one of which is the fact that **bpf_redirect_map**\ * () tries to send packet as a "bulk" to the device. * Return * **XDP_REDIRECT** on success, or **XDP_ABORTED** on error. * * int bpf_sk_redirect_map(struct bpf_map *map, u32 key, u64 flags) * Description * Redirect the packet to the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * Return * **SK_PASS** on success, or **SK_DROP** on error. * * int bpf_sock_map_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags) * Description * Add an entry to, or update a *map* referencing sockets. The * *skops* is used as a new value for the entry associated to * *key*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * If the *map* has eBPF programs (parser and verdict), those will * be inherited by the socket being added. If the socket is * already attached to eBPF programs, this results in an error. * Return * 0 on success, or a negative error in case of failure. * * int bpf_xdp_adjust_meta(struct xdp_buff *xdp_md, int delta) * Description * Adjust the address pointed by *xdp_md*\ **->data_meta** by * *delta* (which can be positive or negative). Note that this * operation modifies the address stored in *xdp_md*\ **->data**, * so the latter must be loaded only after the helper has been * called. * * The use of *xdp_md*\ **->data_meta** is optional and programs * are not required to use it. The rationale is that when the * packet is processed with XDP (e.g. as DoS filter), it is * possible to push further meta data along with it before passing * to the stack, and to give the guarantee that an ingress eBPF * program attached as a TC classifier on the same device can pick * this up for further post-processing. Since TC works with socket * buffers, it remains possible to set from XDP the **mark** or * **priority** pointers, or other pointers for the socket buffer. * Having this scratch space generic and programmable allows for * more flexibility as the user is free to store whatever meta * data they need. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_perf_event_read_value(struct bpf_map *map, u64 flags, struct bpf_perf_event_value *buf, u32 buf_size) * Description * Read the value of a perf event counter, and store it into *buf* * of size *buf_size*. This helper relies on a *map* of type * **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event * counter is selected when *map* is updated with perf event file * descriptors. The *map* is an array whose size is the number of * available CPUs, and each cell contains a value relative to one * CPU. The value to retrieve is indicated by *flags*, that * contains the index of the CPU to look up, masked with * **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to * **BPF_F_CURRENT_CPU** to indicate that the value for the * current CPU should be retrieved. * * This helper behaves in a way close to * **bpf_perf_event_read**\ () helper, save that instead of * just returning the value observed, it fills the *buf* * structure. This allows for additional data to be retrieved: in * particular, the enabled and running times (in *buf*\ * **->enabled** and *buf*\ **->running**, respectively) are * copied. In general, **bpf_perf_event_read_value**\ () is * recommended over **bpf_perf_event_read**\ (), which has some * ABI issues and provides fewer functionalities. * * These values are interesting, because hardware PMU (Performance * Monitoring Unit) counters are limited resources. When there are * more PMU based perf events opened than available counters, * kernel will multiplex these events so each event gets certain * percentage (but not all) of the PMU time. In case that * multiplexing happens, the number of samples or counter value * will not reflect the case compared to when no multiplexing * occurs. This makes comparison between different runs difficult. * Typically, the counter value should be normalized before * comparing to other experiments. The usual normalization is done * as follows. * * :: * * normalized_counter = counter * t_enabled / t_running * * Where t_enabled is the time enabled for event and t_running is * the time running for event since last normalization. The * enabled and running times are accumulated since the perf event * open. To achieve scaling factor between two invocations of an * eBPF program, users can can use CPU id as the key (which is * typical for perf array usage model) to remember the previous * value and do the calculation inside the eBPF program. * Return * 0 on success, or a negative error in case of failure. * * int bpf_perf_prog_read_value(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, u32 buf_size) * Description * For en eBPF program attached to a perf event, retrieve the * value of the event counter associated to *ctx* and store it in * the structure pointed by *buf* and of size *buf_size*. Enabled * and running times are also stored in the structure (see * description of helper **bpf_perf_event_read_value**\ () for * more details). * Return * 0 on success, or a negative error in case of failure. * * int bpf_getsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, char *optval, int optlen) * Description * Emulate a call to **getsockopt()** on the socket associated to * *bpf_socket*, which must be a full socket. The *level* at * which the option resides and the name *optname* of the option * must be specified, see **getsockopt(2)** for more information. * The retrieved value is stored in the structure pointed by * *opval* and of length *optlen*. * * This helper actually implements a subset of **getsockopt()**. * It supports the following *level*\ s: * * * **IPPROTO_TCP**, which supports *optname* * **TCP_CONGESTION**. * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. * Return * 0 on success, or a negative error in case of failure. * * int bpf_override_return(struct pt_reg *regs, u64 rc) * Description * Used for error injection, this helper uses kprobes to override * the return value of the probed function, and to set it to *rc*. * The first argument is the context *regs* on which the kprobe * works. * * This helper works by setting setting the PC (program counter) * to an override function which is run in place of the original * probed function. This means the probed function is not run at * all. The replacement function just returns with the required * value. * * This helper has security implications, and thus is subject to * restrictions. It is only available if the kernel was compiled * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration * option, and in this case it only works on functions tagged with * **ALLOW_ERROR_INJECTION** in the kernel code. * * Also, the helper is only available for the architectures having * the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing, * x86 architecture is the only one to support this feature. * Return * 0 * * int bpf_sock_ops_cb_flags_set(struct bpf_sock_ops *bpf_sock, int argval) * Description * Attempt to set the value of the **bpf_sock_ops_cb_flags** field * for the full TCP socket associated to *bpf_sock_ops* to * *argval*. * * The primary use of this field is to determine if there should * be calls to eBPF programs of type * **BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP * code. A program of the same type can change its value, per * connection and as necessary, when the connection is * established. This field is directly accessible for reading, but * this helper must be used for updates in order to return an * error if an eBPF program tries to set a callback that is not * supported in the current kernel. * * The supported callback values that *argval* can combine are: * * * **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out) * * **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission) * * **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change) * * Here are some examples of where one could call such eBPF * program: * * * When RTO fires. * * When a packet is retransmitted. * * When the connection terminates. * * When a packet is sent. * * When a packet is received. * Return * Code **-EINVAL** if the socket is not a full TCP socket; * otherwise, a positive number containing the bits that could not * be set is returned (which comes down to 0 if all bits were set * as required). * * int bpf_msg_redirect_map(struct sk_msg_buff *msg, struct bpf_map *map, u32 key, u64 flags) * Description * This helper is used in programs implementing policies at the * socket level. If the message *msg* is allowed to pass (i.e. if * the verdict eBPF program returns **SK_PASS**), redirect it to * the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * Return * **SK_PASS** on success, or **SK_DROP** on error. * * int bpf_msg_apply_bytes(struct sk_msg_buff *msg, u32 bytes) * Description * For socket policies, apply the verdict of the eBPF program to * the next *bytes* (number of bytes) of message *msg*. * * For example, this helper can be used in the following cases: * * * A single **sendmsg**\ () or **sendfile**\ () system call * contains multiple logical messages that the eBPF program is * supposed to read and for which it should apply a verdict. * * An eBPF program only cares to read the first *bytes* of a * *msg*. If the message has a large payload, then setting up * and calling the eBPF program repeatedly for all bytes, even * though the verdict is already known, would create unnecessary * overhead. * * When called from within an eBPF program, the helper sets a * counter internal to the BPF infrastructure, that is used to * apply the last verdict to the next *bytes*. If *bytes* is * smaller than the current data being processed from a * **sendmsg**\ () or **sendfile**\ () system call, the first * *bytes* will be sent and the eBPF program will be re-run with * the pointer for start of data pointing to byte number *bytes* * **+ 1**. If *bytes* is larger than the current data being * processed, then the eBPF verdict will be applied to multiple * **sendmsg**\ () or **sendfile**\ () calls until *bytes* are * consumed. * * Note that if a socket closes with the internal counter holding * a non-zero value, this is not a problem because data is not * being buffered for *bytes* and is sent as it is received. * Return * 0 * * int bpf_msg_cork_bytes(struct sk_msg_buff *msg, u32 bytes) * Description * For socket policies, prevent the execution of the verdict eBPF * program for message *msg* until *bytes* (byte number) have been * accumulated. * * This can be used when one needs a specific number of bytes * before a verdict can be assigned, even if the data spans * multiple **sendmsg**\ () or **sendfile**\ () calls. The extreme * case would be a user calling **sendmsg**\ () repeatedly with * 1-byte long message segments. Obviously, this is bad for * performance, but it is still valid. If the eBPF program needs * *bytes* bytes to validate a header, this helper can be used to * prevent the eBPF program to be called again until *bytes* have * been accumulated. * Return * 0 * * int bpf_msg_pull_data(struct sk_msg_buff *msg, u32 start, u32 end, u64 flags) * Description * For socket policies, pull in non-linear data from user space * for *msg* and set pointers *msg*\ **->data** and *msg*\ * **->data_end** to *start* and *end* bytes offsets into *msg*, * respectively. * * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a * *msg* it can only parse data that the (**data**, **data_end**) * pointers have already consumed. For **sendmsg**\ () hooks this * is likely the first scatterlist element. But for calls relying * on the **sendpage** handler (e.g. **sendfile**\ ()) this will * be the range (**0**, **0**) because the data is shared with * user space and by default the objective is to avoid allowing * user space to modify data while (or after) eBPF verdict is * being decided. This helper can be used to pull in data and to * set the start and end pointer to given values. Data will be * copied if necessary (i.e. if data was not linear and if start * and end pointers do not point to the same chunk). * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * All values for *flags* are reserved for future usage, and must * be left at zero. * Return * 0 on success, or a negative error in case of failure. * * int bpf_bind(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len) * Description * Bind the socket associated to *ctx* to the address pointed by * *addr*, of length *addr_len*. This allows for making outgoing * connection from the desired IP address, which can be useful for * example when all processes inside a cgroup should use one * single IP address on a host that has multiple IP configured. * * This helper works for IPv4 and IPv6, TCP and UDP sockets. The * domain (*addr*\ **->sa_family**) must be **AF_INET** (or * **AF_INET6**). Looking for a free port to bind to can be * expensive, therefore binding to port is not permitted by the * helper: *addr*\ **->sin_port** (or **sin6_port**, respectively) * must be set to zero. * Return * 0 on success, or a negative error in case of failure. * * int bpf_xdp_adjust_tail(struct xdp_buff *xdp_md, int delta) * Description * Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is * only possible to shrink the packet as of this writing, * therefore *delta* must be a negative integer. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_get_xfrm_state(struct sk_buff *skb, u32 index, struct bpf_xfrm_state *xfrm_state, u32 size, u64 flags) * Description * Retrieve the XFRM state (IP transform framework, see also * **ip-xfrm(8)**) at *index* in XFRM "security path" for *skb*. * * The retrieved value is stored in the **struct bpf_xfrm_state** * pointed by *xfrm_state* and of length *size*. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * This helper is available only if the kernel was compiled with * **CONFIG_XFRM** configuration option. * Return * 0 on success, or a negative error in case of failure. * * int bpf_get_stack(struct pt_regs *regs, void *buf, u32 size, u64 flags) * Description * Return a user or a kernel stack in bpf program provided buffer. * To achieve this, the helper needs *ctx*, which is a pointer * to the context on which the tracing program is executed. * To store the stacktrace, the bpf program provides *buf* with * a nonnegative *size*. * * The last argument, *flags*, holds the number of stack frames to * skip (from 0 to 255), masked with * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set * the following flags: * * **BPF_F_USER_STACK** * Collect a user space stack instead of a kernel stack. * **BPF_F_USER_BUILD_ID** * Collect buildid+offset instead of ips for user stack, * only valid if **BPF_F_USER_STACK** is also specified. * * **bpf_get_stack**\ () can collect up to * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject * to sufficient large buffer size. Note that * this limit can be controlled with the **sysctl** program, and * that it should be manually increased in order to profile long * user stacks (such as stacks for Java programs). To do so, use: * * :: * * # sysctl kernel.perf_event_max_stack= * Return * A non-negative value equal to or less than *size* on success, * or a negative error in case of failure. * * int bpf_skb_load_bytes_relative(const struct sk_buff *skb, u32 offset, void *to, u32 len, u32 start_header) * Description * This helper is similar to **bpf_skb_load_bytes**\ () in that * it provides an easy way to load *len* bytes from *offset* * from the packet associated to *skb*, into the buffer pointed * by *to*. The difference to **bpf_skb_load_bytes**\ () is that * a fifth argument *start_header* exists in order to select a * base offset to start from. *start_header* can be one of: * * **BPF_HDR_START_MAC** * Base offset to load data from is *skb*'s mac header. * **BPF_HDR_START_NET** * Base offset to load data from is *skb*'s network header. * * In general, "direct packet access" is the preferred method to * access packet data, however, this helper is in particular useful * in socket filters where *skb*\ **->data** does not always point * to the start of the mac header and where "direct packet access" * is not available. * Return * 0 on success, or a negative error in case of failure. * * int bpf_fib_lookup(void *ctx, struct bpf_fib_lookup *params, int plen, u32 flags) * Description * Do FIB lookup in kernel tables using parameters in *params*. * If lookup is successful and result shows packet is to be * forwarded, the neighbor tables are searched for the nexthop. * If successful (ie., FIB lookup shows forwarding and nexthop * is resolved), the nexthop address is returned in ipv4_dst * or ipv6_dst based on family, smac is set to mac address of * egress device, dmac is set to nexthop mac address, rt_metric * is set to metric from route (IPv4/IPv6 only), and ifindex * is set to the device index of the nexthop from the FIB lookup. * * *plen* argument is the size of the passed in struct. * *flags* argument can be a combination of one or more of the * following values: * * **BPF_FIB_LOOKUP_DIRECT** * Do a direct table lookup vs full lookup using FIB * rules. * **BPF_FIB_LOOKUP_OUTPUT** * Perform lookup from an egress perspective (default is * ingress). * * *ctx* is either **struct xdp_md** for XDP programs or * **struct sk_buff** tc cls_act programs. * Return * * < 0 if any input argument is invalid * * 0 on success (packet is forwarded, nexthop neighbor exists) * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the * packet is not forwarded or needs assist from full stack * * int bpf_sock_hash_update(struct bpf_sock_ops_kern *skops, struct bpf_map *map, void *key, u64 flags) * Description * Add an entry to, or update a sockhash *map* referencing sockets. * The *skops* is used as a new value for the entry associated to * *key*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * If the *map* has eBPF programs (parser and verdict), those will * be inherited by the socket being added. If the socket is * already attached to eBPF programs, this results in an error. * Return * 0 on success, or a negative error in case of failure. * * int bpf_msg_redirect_hash(struct sk_msg_buff *msg, struct bpf_map *map, void *key, u64 flags) * Description * This helper is used in programs implementing policies at the * socket level. If the message *msg* is allowed to pass (i.e. if * the verdict eBPF program returns **SK_PASS**), redirect it to * the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * Return * **SK_PASS** on success, or **SK_DROP** on error. * * int bpf_sk_redirect_hash(struct sk_buff *skb, struct bpf_map *map, void *key, u64 flags) * Description * This helper is used in programs implementing policies at the * skb socket level. If the sk_buff *skb* is allowed to pass (i.e. * if the verdeict eBPF program returns **SK_PASS**), redirect it * to the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress otherwise). This is the only flag supported for now. * Return * **SK_PASS** on success, or **SK_DROP** on error. * * int bpf_lwt_push_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len) * Description * Encapsulate the packet associated to *skb* within a Layer 3 * protocol header. This header is provided in the buffer at * address *hdr*, with *len* its size in bytes. *type* indicates * the protocol of the header and can be one of: * * **BPF_LWT_ENCAP_SEG6** * IPv6 encapsulation with Segment Routing Header * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH, * the IPv6 header is computed by the kernel. * **BPF_LWT_ENCAP_SEG6_INLINE** * Only works if *skb* contains an IPv6 packet. Insert a * Segment Routing Header (**struct ipv6_sr_hdr**) inside * the IPv6 header. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_lwt_seg6_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len) * Description * Store *len* bytes from address *from* into the packet * associated to *skb*, at *offset*. Only the flags, tag and TLVs * inside the outermost IPv6 Segment Routing Header can be * modified through this helper. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_lwt_seg6_adjust_srh(struct sk_buff *skb, u32 offset, s32 delta) * Description * Adjust the size allocated to TLVs in the outermost IPv6 * Segment Routing Header contained in the packet associated to * *skb*, at position *offset* by *delta* bytes. Only offsets * after the segments are accepted. *delta* can be as well * positive (growing) as negative (shrinking). * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_lwt_seg6_action(struct sk_buff *skb, u32 action, void *param, u32 param_len) * Description * Apply an IPv6 Segment Routing action of type *action* to the * packet associated to *skb*. Each action takes a parameter * contained at address *param*, and of length *param_len* bytes. * *action* can be one of: * * **SEG6_LOCAL_ACTION_END_X** * End.X action: Endpoint with Layer-3 cross-connect. * Type of *param*: **struct in6_addr**. * **SEG6_LOCAL_ACTION_END_T** * End.T action: Endpoint with specific IPv6 table lookup. * Type of *param*: **int**. * **SEG6_LOCAL_ACTION_END_B6** * End.B6 action: Endpoint bound to an SRv6 policy. * Type of param: **struct ipv6_sr_hdr**. * **SEG6_LOCAL_ACTION_END_B6_ENCAP** * End.B6.Encap action: Endpoint bound to an SRv6 * encapsulation policy. * Type of param: **struct ipv6_sr_hdr**. * * A call to this helper is susceptible to change the underlaying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_rc_keydown(void *ctx, u32 protocol, u64 scancode, u32 toggle) * Description * This helper is used in programs implementing IR decoding, to * report a successfully decoded key press with *scancode*, * *toggle* value in the given *protocol*. The scancode will be * translated to a keycode using the rc keymap, and reported as * an input key down event. After a period a key up event is * generated. This period can be extended by calling either * **bpf_rc_keydown** () again with the same values, or calling * **bpf_rc_repeat** (). * * Some protocols include a toggle bit, in case the button was * released and pressed again between consecutive scancodes. * * The *ctx* should point to the lirc sample as passed into * the program. * * The *protocol* is the decoded protocol number (see * **enum rc_proto** for some predefined values). * * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". * Return * 0 * * int bpf_rc_repeat(void *ctx) * Description * This helper is used in programs implementing IR decoding, to * report a successfully decoded repeat key message. This delays * the generation of a key up event for previously generated * key down event. * * Some IR protocols like NEC have a special IR message for * repeating last button, for when a button is held down. * * The *ctx* should point to the lirc sample as passed into * the program. * * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". * Return * 0 * * uint64_t bpf_skb_cgroup_id(struct sk_buff *skb) * Description * Return the cgroup v2 id of the socket associated with the *skb*. * This is roughly similar to the **bpf_get_cgroup_classid**\ () * helper for cgroup v1 by providing a tag resp. identifier that * can be matched on or used for map lookups e.g. to implement * policy. The cgroup v2 id of a given path in the hierarchy is * exposed in user space through the f_handle API in order to get * to the same 64-bit id. * * This helper can be used on TC egress path, but not on ingress, * and is available only if the kernel was compiled with the * **CONFIG_SOCK_CGROUP_DATA** configuration option. * Return * The id is returned or 0 in case the id could not be retrieved. * * u64 bpf_skb_ancestor_cgroup_id(struct sk_buff *skb, int ancestor_level) * Description * Return id of cgroup v2 that is ancestor of cgroup associated * with the *skb* at the *ancestor_level*. The root cgroup is at * *ancestor_level* zero and each step down the hierarchy * increments the level. If *ancestor_level* == level of cgroup * associated with *skb*, then return value will be same as that * of **bpf_skb_cgroup_id**\ (). * * The helper is useful to implement policies based on cgroups * that are upper in hierarchy than immediate cgroup associated * with *skb*. * * The format of returned id and helper limitations are same as in * **bpf_skb_cgroup_id**\ (). * Return * The id is returned or 0 in case the id could not be retrieved. * * u64 bpf_get_current_cgroup_id(void) * Return * A 64-bit integer containing the current cgroup id based * on the cgroup within which the current task is running. * * void* get_local_storage(void *map, u64 flags) * Description * Get the pointer to the local storage area. * The type and the size of the local storage is defined * by the *map* argument. * The *flags* meaning is specific for each map type, * and has to be 0 for cgroup local storage. * * Depending on the bpf program type, a local storage area * can be shared between multiple instances of the bpf program, * running simultaneously. * * A user should care about the synchronization by himself. * For example, by using the BPF_STX_XADD instruction to alter * the shared data. * Return * Pointer to the local storage area. * * int bpf_sk_select_reuseport(struct sk_reuseport_md *reuse, struct bpf_map *map, void *key, u64 flags) * Description * Select a SO_REUSEPORT sk from a BPF_MAP_TYPE_REUSEPORT_ARRAY map * It checks the selected sk is matching the incoming * request in the skb. * Return * 0 on success, or a negative error in case of failure. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ FN(map_lookup_elem), \ FN(map_update_elem), \ FN(map_delete_elem), \ FN(probe_read), \ FN(ktime_get_ns), \ FN(trace_printk), \ FN(get_prandom_u32), \ FN(get_smp_processor_id), \ FN(skb_store_bytes), \ FN(l3_csum_replace), \ FN(l4_csum_replace), \ FN(tail_call), \ FN(clone_redirect), \ FN(get_current_pid_tgid), \ FN(get_current_uid_gid), \ FN(get_current_comm), \ FN(get_cgroup_classid), \ FN(skb_vlan_push), \ FN(skb_vlan_pop), \ FN(skb_get_tunnel_key), \ FN(skb_set_tunnel_key), \ FN(perf_event_read), \ FN(redirect), \ FN(get_route_realm), \ FN(perf_event_output), \ FN(skb_load_bytes), \ FN(get_stackid), \ FN(csum_diff), \ FN(skb_get_tunnel_opt), \ FN(skb_set_tunnel_opt), \ FN(skb_change_proto), \ FN(skb_change_type), \ FN(skb_under_cgroup), \ FN(get_hash_recalc), \ FN(get_current_task), \ FN(probe_write_user), \ FN(current_task_under_cgroup), \ FN(skb_change_tail), \ FN(skb_pull_data), \ FN(csum_update), \ FN(set_hash_invalid), \ FN(get_numa_node_id), \ FN(skb_change_head), \ FN(xdp_adjust_head), \ FN(probe_read_str), \ FN(get_socket_cookie), \ FN(get_socket_uid), \ FN(set_hash), \ FN(setsockopt), \ FN(skb_adjust_room), \ FN(redirect_map), \ FN(sk_redirect_map), \ FN(sock_map_update), \ FN(xdp_adjust_meta), \ FN(perf_event_read_value), \ FN(perf_prog_read_value), \ FN(getsockopt), \ FN(override_return), \ FN(sock_ops_cb_flags_set), \ FN(msg_redirect_map), \ FN(msg_apply_bytes), \ FN(msg_cork_bytes), \ FN(msg_pull_data), \ FN(bind), \ FN(xdp_adjust_tail), \ FN(skb_get_xfrm_state), \ FN(get_stack), \ FN(skb_load_bytes_relative), \ FN(fib_lookup), \ FN(sock_hash_update), \ FN(msg_redirect_hash), \ FN(sk_redirect_hash), \ FN(lwt_push_encap), \ FN(lwt_seg6_store_bytes), \ FN(lwt_seg6_adjust_srh), \ FN(lwt_seg6_action), \ FN(rc_repeat), \ FN(rc_keydown), \ FN(skb_cgroup_id), \ FN(get_current_cgroup_id), \ FN(get_local_storage), \ FN(sk_select_reuseport), \ FN(skb_ancestor_cgroup_id), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call */ #define __BPF_ENUM_FN(x) BPF_FUNC_ ## x enum bpf_func_id { __BPF_FUNC_MAPPER(__BPF_ENUM_FN) __BPF_FUNC_MAX_ID, }; #undef __BPF_ENUM_FN /* All flags used by eBPF helper functions, placed here. */ /* BPF_FUNC_skb_store_bytes flags. */ #define BPF_F_RECOMPUTE_CSUM (1ULL << 0) #define BPF_F_INVALIDATE_HASH (1ULL << 1) /* BPF_FUNC_l3_csum_replace and BPF_FUNC_l4_csum_replace flags. * First 4 bits are for passing the header field size. */ #define BPF_F_HDR_FIELD_MASK 0xfULL /* BPF_FUNC_l4_csum_replace flags. */ #define BPF_F_PSEUDO_HDR (1ULL << 4) #define BPF_F_MARK_MANGLED_0 (1ULL << 5) #define BPF_F_MARK_ENFORCE (1ULL << 6) /* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */ #define BPF_F_INGRESS (1ULL << 0) /* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */ #define BPF_F_TUNINFO_IPV6 (1ULL << 0) /* flags for both BPF_FUNC_get_stackid and BPF_FUNC_get_stack. */ #define BPF_F_SKIP_FIELD_MASK 0xffULL #define BPF_F_USER_STACK (1ULL << 8) /* flags used by BPF_FUNC_get_stackid only. */ #define BPF_F_FAST_STACK_CMP (1ULL << 9) #define BPF_F_REUSE_STACKID (1ULL << 10) /* flags used by BPF_FUNC_get_stack only. */ #define BPF_F_USER_BUILD_ID (1ULL << 11) /* BPF_FUNC_skb_set_tunnel_key flags. */ #define BPF_F_ZERO_CSUM_TX (1ULL << 1) #define BPF_F_DONT_FRAGMENT (1ULL << 2) #define BPF_F_SEQ_NUMBER (1ULL << 3) /* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and * BPF_FUNC_perf_event_read_value flags. */ #define BPF_F_INDEX_MASK 0xffffffffULL #define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK /* BPF_FUNC_perf_event_output for sk_buff input context. */ #define BPF_F_CTXLEN_MASK (0xfffffULL << 32) /* Mode for BPF_FUNC_skb_adjust_room helper. */ enum bpf_adj_room_mode { BPF_ADJ_ROOM_NET, }; /* Mode for BPF_FUNC_skb_load_bytes_relative helper. */ enum bpf_hdr_start_off { BPF_HDR_START_MAC, BPF_HDR_START_NET, }; /* Encapsulation type for BPF_FUNC_lwt_push_encap helper. */ enum bpf_lwt_encap_mode { BPF_LWT_ENCAP_SEG6, BPF_LWT_ENCAP_SEG6_INLINE }; /* user accessible mirror of in-kernel sk_buff. * new fields can only be added to the end of this structure */ struct __sk_buff { __u32 len; __u32 pkt_type; __u32 mark; __u32 queue_mapping; __u32 protocol; __u32 vlan_present; __u32 vlan_tci; __u32 vlan_proto; __u32 priority; __u32 ingress_ifindex; __u32 ifindex; __u32 tc_index; __u32 cb[5]; __u32 hash; __u32 tc_classid; __u32 data; __u32 data_end; __u32 napi_id; /* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */ __u32 family; __u32 remote_ip4; /* Stored in network byte order */ __u32 local_ip4; /* Stored in network byte order */ __u32 remote_ip6[4]; /* Stored in network byte order */ __u32 local_ip6[4]; /* Stored in network byte order */ __u32 remote_port; /* Stored in network byte order */ __u32 local_port; /* stored in host byte order */ /* ... here. */ __u32 data_meta; struct bpf_flow_keys *flow_keys; }; struct bpf_tunnel_key { __u32 tunnel_id; union { __u32 remote_ipv4; __u32 remote_ipv6[4]; }; __u8 tunnel_tos; __u8 tunnel_ttl; __u16 tunnel_ext; /* Padding, future use. */ __u32 tunnel_label; }; /* user accessible mirror of in-kernel xfrm_state. * new fields can only be added to the end of this structure */ struct bpf_xfrm_state { __u32 reqid; __u32 spi; /* Stored in network byte order */ __u16 family; __u16 ext; /* Padding, future use. */ union { __u32 remote_ipv4; /* Stored in network byte order */ __u32 remote_ipv6[4]; /* Stored in network byte order */ }; }; /* Generic BPF return codes which all BPF program types may support. * The values are binary compatible with their TC_ACT_* counter-part to * provide backwards compatibility with existing SCHED_CLS and SCHED_ACT * programs. * * XDP is handled seprately, see XDP_*. */ enum bpf_ret_code { BPF_OK = 0, /* 1 reserved */ BPF_DROP = 2, /* 3-6 reserved */ BPF_REDIRECT = 7, /* >127 are reserved for prog type specific return codes */ }; struct bpf_sock { __u32 bound_dev_if; __u32 family; __u32 type; __u32 protocol; __u32 mark; __u32 priority; __u32 src_ip4; /* Allows 1,2,4-byte read. * Stored in network byte order. */ __u32 src_ip6[4]; /* Allows 1,2,4-byte read. * Stored in network byte order. */ __u32 src_port; /* Allows 4-byte read. * Stored in host byte order */ }; #define XDP_PACKET_HEADROOM 256 /* User return codes for XDP prog type. * A valid XDP program must return one of these defined values. All other * return codes are reserved for future use. Unknown return codes will * result in packet drops and a warning via bpf_warn_invalid_xdp_action(). */ enum xdp_action { XDP_ABORTED = 0, XDP_DROP, XDP_PASS, XDP_TX, XDP_REDIRECT, }; /* user accessible metadata for XDP packet hook * new fields must be added to the end of this structure */ struct xdp_md { __u32 data; __u32 data_end; __u32 data_meta; /* Below access go through struct xdp_rxq_info */ __u32 ingress_ifindex; /* rxq->dev->ifindex */ __u32 rx_queue_index; /* rxq->queue_index */ }; enum sk_action { SK_DROP = 0, SK_PASS, }; /* user accessible metadata for SK_MSG packet hook, new fields must * be added to the end of this structure */ struct sk_msg_md { void *data; void *data_end; __u32 family; __u32 remote_ip4; /* Stored in network byte order */ __u32 local_ip4; /* Stored in network byte order */ __u32 remote_ip6[4]; /* Stored in network byte order */ __u32 local_ip6[4]; /* Stored in network byte order */ __u32 remote_port; /* Stored in network byte order */ __u32 local_port; /* stored in host byte order */ }; struct sk_reuseport_md { /* * Start of directly accessible data. It begins from * the tcp/udp header. */ void *data; void *data_end; /* End of directly accessible data */ /* * Total length of packet (starting from the tcp/udp header). * Note that the directly accessible bytes (data_end - data) * could be less than this "len". Those bytes could be * indirectly read by a helper "bpf_skb_load_bytes()". */ __u32 len; /* * Eth protocol in the mac header (network byte order). e.g. * ETH_P_IP(0x0800) and ETH_P_IPV6(0x86DD) */ __u32 eth_protocol; __u32 ip_protocol; /* IP protocol. e.g. IPPROTO_TCP, IPPROTO_UDP */ __u32 bind_inany; /* Is sock bound to an INANY address? */ __u32 hash; /* A hash of the packet 4 tuples */ }; #define BPF_TAG_SIZE 8 struct bpf_prog_info { __u32 type; __u32 id; __u8 tag[BPF_TAG_SIZE]; __u32 jited_prog_len; __u32 xlated_prog_len; __aligned_u64 jited_prog_insns; __aligned_u64 xlated_prog_insns; __u64 load_time; /* ns since boottime */ __u32 created_by_uid; __u32 nr_map_ids; __aligned_u64 map_ids; char name[BPF_OBJ_NAME_LEN]; __u32 ifindex; __u32 gpl_compatible:1; __u64 netns_dev; __u64 netns_ino; __u32 nr_jited_ksyms; __u32 nr_jited_func_lens; __aligned_u64 jited_ksyms; __aligned_u64 jited_func_lens; } __attribute__((aligned(8))); struct bpf_map_info { __u32 type; __u32 id; __u32 key_size; __u32 value_size; __u32 max_entries; __u32 map_flags; char name[BPF_OBJ_NAME_LEN]; __u32 ifindex; __u32 :32; __u64 netns_dev; __u64 netns_ino; __u32 btf_id; __u32 btf_key_type_id; __u32 btf_value_type_id; } __attribute__((aligned(8))); struct bpf_btf_info { __aligned_u64 btf; __u32 btf_size; __u32 id; } __attribute__((aligned(8))); /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed * by user and intended to be used by socket (e.g. to bind to, depends on * attach attach type). */ struct bpf_sock_addr { __u32 user_family; /* Allows 4-byte read, but no write. */ __u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write. * Stored in network byte order. */ __u32 user_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write. * Stored in network byte order. */ __u32 user_port; /* Allows 4-byte read and write. * Stored in network byte order */ __u32 family; /* Allows 4-byte read, but no write */ __u32 type; /* Allows 4-byte read, but no write */ __u32 protocol; /* Allows 4-byte read, but no write */ __u32 msg_src_ip4; /* Allows 1,2,4-byte read an 4-byte write. * Stored in network byte order. */ __u32 msg_src_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write. * Stored in network byte order. */ }; /* User bpf_sock_ops struct to access socket values and specify request ops * and their replies. * Some of this fields are in network (bigendian) byte order and may need * to be converted before use (bpf_ntohl() defined in samples/bpf/bpf_endian.h). * New fields can only be added at the end of this structure */ struct bpf_sock_ops { __u32 op; union { __u32 args[4]; /* Optionally passed to bpf program */ __u32 reply; /* Returned by bpf program */ __u32 replylong[4]; /* Optionally returned by bpf prog */ }; __u32 family; __u32 remote_ip4; /* Stored in network byte order */ __u32 local_ip4; /* Stored in network byte order */ __u32 remote_ip6[4]; /* Stored in network byte order */ __u32 local_ip6[4]; /* Stored in network byte order */ __u32 remote_port; /* Stored in network byte order */ __u32 local_port; /* stored in host byte order */ __u32 is_fullsock; /* Some TCP fields are only valid if * there is a full socket. If not, the * fields read as zero. */ __u32 snd_cwnd; __u32 srtt_us; /* Averaged RTT << 3 in usecs */ __u32 bpf_sock_ops_cb_flags; /* flags defined in uapi/linux/tcp.h */ __u32 state; __u32 rtt_min; __u32 snd_ssthresh; __u32 rcv_nxt; __u32 snd_nxt; __u32 snd_una; __u32 mss_cache; __u32 ecn_flags; __u32 rate_delivered; __u32 rate_interval_us; __u32 packets_out; __u32 retrans_out; __u32 total_retrans; __u32 segs_in; __u32 data_segs_in; __u32 segs_out; __u32 data_segs_out; __u32 lost_out; __u32 sacked_out; __u32 sk_txhash; __u64 bytes_received; __u64 bytes_acked; }; /* Definitions for bpf_sock_ops_cb_flags */ #define BPF_SOCK_OPS_RTO_CB_FLAG (1<<0) #define BPF_SOCK_OPS_RETRANS_CB_FLAG (1<<1) #define BPF_SOCK_OPS_STATE_CB_FLAG (1<<2) #define BPF_SOCK_OPS_ALL_CB_FLAGS 0x7 /* Mask of all currently * supported cb flags */ /* List of known BPF sock_ops operators. * New entries can only be added at the end */ enum { BPF_SOCK_OPS_VOID, BPF_SOCK_OPS_TIMEOUT_INIT, /* Should return SYN-RTO value to use or * -1 if default value should be used */ BPF_SOCK_OPS_RWND_INIT, /* Should return initial advertized * window (in packets) or -1 if default * value should be used */ BPF_SOCK_OPS_TCP_CONNECT_CB, /* Calls BPF program right before an * active connection is initialized */ BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB, /* Calls BPF program when an * active connection is * established */ BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB, /* Calls BPF program when a * passive connection is * established */ BPF_SOCK_OPS_NEEDS_ECN, /* If connection's congestion control * needs ECN */ BPF_SOCK_OPS_BASE_RTT, /* Get base RTT. The correct value is * based on the path and may be * dependent on the congestion control * algorithm. In general it indicates * a congestion threshold. RTTs above * this indicate congestion */ BPF_SOCK_OPS_RTO_CB, /* Called when an RTO has triggered. * Arg1: value of icsk_retransmits * Arg2: value of icsk_rto * Arg3: whether RTO has expired */ BPF_SOCK_OPS_RETRANS_CB, /* Called when skb is retransmitted. * Arg1: sequence number of 1st byte * Arg2: # segments * Arg3: return value of * tcp_transmit_skb (0 => success) */ BPF_SOCK_OPS_STATE_CB, /* Called when TCP changes state. * Arg1: old_state * Arg2: new_state */ BPF_SOCK_OPS_TCP_LISTEN_CB, /* Called on listen(2), right after * socket transition to LISTEN state. */ }; /* List of TCP states. There is a build check in net/ipv4/tcp.c to detect * changes between the TCP and BPF versions. Ideally this should never happen. * If it does, we need to add code to convert them before calling * the BPF sock_ops function. */ enum { BPF_TCP_ESTABLISHED = 1, BPF_TCP_SYN_SENT, BPF_TCP_SYN_RECV, BPF_TCP_FIN_WAIT1, BPF_TCP_FIN_WAIT2, BPF_TCP_TIME_WAIT, BPF_TCP_CLOSE, BPF_TCP_CLOSE_WAIT, BPF_TCP_LAST_ACK, BPF_TCP_LISTEN, BPF_TCP_CLOSING, /* Now a valid state */ BPF_TCP_NEW_SYN_RECV, BPF_TCP_MAX_STATES /* Leave at the end! */ }; #define TCP_BPF_IW 1001 /* Set TCP initial congestion window */ #define TCP_BPF_SNDCWND_CLAMP 1002 /* Set sndcwnd_clamp */ struct bpf_perf_event_value { __u64 counter; __u64 enabled; __u64 running; }; #define BPF_DEVCG_ACC_MKNOD (1ULL << 0) #define BPF_DEVCG_ACC_READ (1ULL << 1) #define BPF_DEVCG_ACC_WRITE (1ULL << 2) #define BPF_DEVCG_DEV_BLOCK (1ULL << 0) #define BPF_DEVCG_DEV_CHAR (1ULL << 1) struct bpf_cgroup_dev_ctx { /* access_type encoded as (BPF_DEVCG_ACC_* << 16) | BPF_DEVCG_DEV_* */ __u32 access_type; __u32 major; __u32 minor; }; struct bpf_raw_tracepoint_args { __u64 args[0]; }; /* DIRECT: Skip the FIB rules and go to FIB table associated with device * OUTPUT: Do lookup from egress perspective; default is ingress */ #define BPF_FIB_LOOKUP_DIRECT BIT(0) #define BPF_FIB_LOOKUP_OUTPUT BIT(1) enum { BPF_FIB_LKUP_RET_SUCCESS, /* lookup successful */ BPF_FIB_LKUP_RET_BLACKHOLE, /* dest is blackholed; can be dropped */ BPF_FIB_LKUP_RET_UNREACHABLE, /* dest is unreachable; can be dropped */ BPF_FIB_LKUP_RET_PROHIBIT, /* dest not allowed; can be dropped */ BPF_FIB_LKUP_RET_NOT_FWDED, /* packet is not forwarded */ BPF_FIB_LKUP_RET_FWD_DISABLED, /* fwding is not enabled on ingress */ BPF_FIB_LKUP_RET_UNSUPP_LWT, /* fwd requires encapsulation */ BPF_FIB_LKUP_RET_NO_NEIGH, /* no neighbor entry for nh */ BPF_FIB_LKUP_RET_FRAG_NEEDED, /* fragmentation required to fwd */ }; struct bpf_fib_lookup { /* input: network family for lookup (AF_INET, AF_INET6) * output: network family of egress nexthop */ __u8 family; /* set if lookup is to consider L4 data - e.g., FIB rules */ __u8 l4_protocol; __be16 sport; __be16 dport; /* total length of packet from network header - used for MTU check */ __u16 tot_len; /* input: L3 device index for lookup * output: device index from FIB lookup */ __u32 ifindex; union { /* inputs to lookup */ __u8 tos; /* AF_INET */ __be32 flowinfo; /* AF_INET6, flow_label + priority */ /* output: metric of fib result (IPv4/IPv6 only) */ __u32 rt_metric; }; union { __be32 ipv4_src; __u32 ipv6_src[4]; /* in6_addr; network order */ }; /* input to bpf_fib_lookup, ipv{4,6}_dst is destination address in * network header. output: bpf_fib_lookup sets to gateway address * if FIB lookup returns gateway route */ union { __be32 ipv4_dst; __u32 ipv6_dst[4]; /* in6_addr; network order */ }; /* output */ __be16 h_vlan_proto; __be16 h_vlan_TCI; __u8 smac[6]; /* ETH_ALEN */ __u8 dmac[6]; /* ETH_ALEN */ }; enum bpf_task_fd_type { BPF_FD_TYPE_RAW_TRACEPOINT, /* tp name */ BPF_FD_TYPE_TRACEPOINT, /* tp name */ BPF_FD_TYPE_KPROBE, /* (symbol + offset) or addr */ BPF_FD_TYPE_KRETPROBE, /* (symbol + offset) or addr */ BPF_FD_TYPE_UPROBE, /* filename + offset */ BPF_FD_TYPE_URETPROBE, /* filename + offset */ }; struct bpf_flow_keys { __u16 nhoff; __u16 thoff; __u16 addr_proto; /* ETH_P_* of valid addrs */ __u8 is_frag; __u8 is_first_frag; __u8 is_encap; __u8 ip_proto; __be16 n_proto; __be16 sport; __be16 dport; union { struct { __be32 ipv4_src; __be32 ipv4_dst; }; struct { __u32 ipv6_src[4]; /* in6_addr; network order */ __u32 ipv6_dst[4]; /* in6_addr; network order */ }; }; }; #endif /* _UAPI__LINUX_BPF_H__ */ sysdig-0.26.4/userspace/libscap/compat/bpf_common.h000066400000000000000000000025461352731327100223020ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI__LINUX_BPF_COMMON_H__ #define _UAPI__LINUX_BPF_COMMON_H__ /* Instruction classes */ #define BPF_CLASS(code) ((code) & 0x07) #define BPF_LD 0x00 #define BPF_LDX 0x01 #define BPF_ST 0x02 #define BPF_STX 0x03 #define BPF_ALU 0x04 #define BPF_JMP 0x05 #define BPF_RET 0x06 #define BPF_MISC 0x07 /* ld/ldx fields */ #define BPF_SIZE(code) ((code) & 0x18) #define BPF_W 0x00 /* 32-bit */ #define BPF_H 0x08 /* 16-bit */ #define BPF_B 0x10 /* 8-bit */ /* eBPF BPF_DW 0x18 64-bit */ #define BPF_MODE(code) ((code) & 0xe0) #define BPF_IMM 0x00 #define BPF_ABS 0x20 #define BPF_IND 0x40 #define BPF_MEM 0x60 #define BPF_LEN 0x80 #define BPF_MSH 0xa0 /* alu/jmp fields */ #define BPF_OP(code) ((code) & 0xf0) #define BPF_ADD 0x00 #define BPF_SUB 0x10 #define BPF_MUL 0x20 #define BPF_DIV 0x30 #define BPF_OR 0x40 #define BPF_AND 0x50 #define BPF_LSH 0x60 #define BPF_RSH 0x70 #define BPF_NEG 0x80 #define BPF_MOD 0x90 #define BPF_XOR 0xa0 #define BPF_JA 0x00 #define BPF_JEQ 0x10 #define BPF_JGT 0x20 #define BPF_JGE 0x30 #define BPF_JSET 0x40 #define BPF_SRC(code) ((code) & 0x08) #define BPF_K 0x00 #define BPF_X 0x08 #ifndef BPF_MAXINSNS #define BPF_MAXINSNS 4096 #endif #endif /* _UAPI__LINUX_BPF_COMMON_H__ */ sysdig-0.26.4/userspace/libscap/compat/misc.h000066400000000000000000000022711352731327100211110ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #ifndef __COMPAT_MISC_H #define __COMPAT_MISC_H #include "bpf.h" #ifndef __NR_bpf #ifdef __x86_64__ #define __NR_bpf 321 #else #define __NR_bpf 357 #endif /* __x86_64__ */ #endif /* __NR_bpf */ #ifndef CLOCK_BOOTTIME #define CLOCK_BOOTTIME 7 #endif static int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int size) { return syscall(__NR_bpf, cmd, attr, size); } static int sys_perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags) { return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); } #endif sysdig-0.26.4/userspace/libscap/compat/perf_event.h000066400000000000000000001003241352731327100223110ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * Performance events: * * Copyright (C) 2008-2009, Thomas Gleixner * Copyright (C) 2008-2011, Red Hat, Inc., Ingo Molnar * Copyright (C) 2008-2011, Red Hat, Inc., Peter Zijlstra * * Data type definitions, declarations, prototypes. * * Started by: Thomas Gleixner and Ingo Molnar * * For licencing details see kernel-base/COPYING */ #ifndef _UAPI_LINUX_PERF_EVENT_H #define _UAPI_LINUX_PERF_EVENT_H #include #include #include /* * User-space ABI bits: */ /* * attr.type */ enum perf_type_id { PERF_TYPE_HARDWARE = 0, PERF_TYPE_SOFTWARE = 1, PERF_TYPE_TRACEPOINT = 2, PERF_TYPE_HW_CACHE = 3, PERF_TYPE_RAW = 4, PERF_TYPE_BREAKPOINT = 5, PERF_TYPE_MAX, /* non-ABI */ }; /* * Generalized performance event event_id types, used by the * attr.event_id parameter of the sys_perf_event_open() * syscall: */ enum perf_hw_id { /* * Common hardware events, generalized by the kernel: */ PERF_COUNT_HW_CPU_CYCLES = 0, PERF_COUNT_HW_INSTRUCTIONS = 1, PERF_COUNT_HW_CACHE_REFERENCES = 2, PERF_COUNT_HW_CACHE_MISSES = 3, PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, PERF_COUNT_HW_BRANCH_MISSES = 5, PERF_COUNT_HW_BUS_CYCLES = 6, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7, PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8, PERF_COUNT_HW_REF_CPU_CYCLES = 9, PERF_COUNT_HW_MAX, /* non-ABI */ }; /* * Generalized hardware cache events: * * { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x * { read, write, prefetch } x * { accesses, misses } */ enum perf_hw_cache_id { PERF_COUNT_HW_CACHE_L1D = 0, PERF_COUNT_HW_CACHE_L1I = 1, PERF_COUNT_HW_CACHE_LL = 2, PERF_COUNT_HW_CACHE_DTLB = 3, PERF_COUNT_HW_CACHE_ITLB = 4, PERF_COUNT_HW_CACHE_BPU = 5, PERF_COUNT_HW_CACHE_NODE = 6, PERF_COUNT_HW_CACHE_MAX, /* non-ABI */ }; enum perf_hw_cache_op_id { PERF_COUNT_HW_CACHE_OP_READ = 0, PERF_COUNT_HW_CACHE_OP_WRITE = 1, PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */ }; enum perf_hw_cache_op_result_id { PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, PERF_COUNT_HW_CACHE_RESULT_MISS = 1, PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */ }; /* * Special "software" events provided by the kernel, even if the hardware * does not support performance events. These events measure various * physical and sw events of the kernel (and allow the profiling of them as * well): */ enum perf_sw_ids { PERF_COUNT_SW_CPU_CLOCK = 0, PERF_COUNT_SW_TASK_CLOCK = 1, PERF_COUNT_SW_PAGE_FAULTS = 2, PERF_COUNT_SW_CONTEXT_SWITCHES = 3, PERF_COUNT_SW_CPU_MIGRATIONS = 4, PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, PERF_COUNT_SW_ALIGNMENT_FAULTS = 7, PERF_COUNT_SW_EMULATION_FAULTS = 8, PERF_COUNT_SW_DUMMY = 9, PERF_COUNT_SW_BPF_OUTPUT = 10, PERF_COUNT_SW_MAX, /* non-ABI */ }; /* * Bits that can be set in attr.sample_type to request information * in the overflow packets. */ enum perf_event_sample_format { PERF_SAMPLE_IP = 1U << 0, PERF_SAMPLE_TID = 1U << 1, PERF_SAMPLE_TIME = 1U << 2, PERF_SAMPLE_ADDR = 1U << 3, PERF_SAMPLE_READ = 1U << 4, PERF_SAMPLE_CALLCHAIN = 1U << 5, PERF_SAMPLE_ID = 1U << 6, PERF_SAMPLE_CPU = 1U << 7, PERF_SAMPLE_PERIOD = 1U << 8, PERF_SAMPLE_STREAM_ID = 1U << 9, PERF_SAMPLE_RAW = 1U << 10, PERF_SAMPLE_BRANCH_STACK = 1U << 11, PERF_SAMPLE_REGS_USER = 1U << 12, PERF_SAMPLE_STACK_USER = 1U << 13, PERF_SAMPLE_WEIGHT = 1U << 14, PERF_SAMPLE_DATA_SRC = 1U << 15, PERF_SAMPLE_IDENTIFIER = 1U << 16, PERF_SAMPLE_TRANSACTION = 1U << 17, PERF_SAMPLE_REGS_INTR = 1U << 18, PERF_SAMPLE_PHYS_ADDR = 1U << 19, PERF_SAMPLE_MAX = 1U << 20, /* non-ABI */ __PERF_SAMPLE_CALLCHAIN_EARLY = 1ULL << 63, /* non-ABI; internal use */ }; /* * values to program into branch_sample_type when PERF_SAMPLE_BRANCH is set * * If the user does not pass priv level information via branch_sample_type, * the kernel uses the event's priv level. Branch and event priv levels do * not have to match. Branch priv level is checked for permissions. * * The branch types can be combined, however BRANCH_ANY covers all types * of branches and therefore it supersedes all the other types. */ enum perf_branch_sample_type_shift { PERF_SAMPLE_BRANCH_USER_SHIFT = 0, /* user branches */ PERF_SAMPLE_BRANCH_KERNEL_SHIFT = 1, /* kernel branches */ PERF_SAMPLE_BRANCH_HV_SHIFT = 2, /* hypervisor branches */ PERF_SAMPLE_BRANCH_ANY_SHIFT = 3, /* any branch types */ PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT = 4, /* any call branch */ PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT = 5, /* any return branch */ PERF_SAMPLE_BRANCH_IND_CALL_SHIFT = 6, /* indirect calls */ PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT = 7, /* transaction aborts */ PERF_SAMPLE_BRANCH_IN_TX_SHIFT = 8, /* in transaction */ PERF_SAMPLE_BRANCH_NO_TX_SHIFT = 9, /* not in transaction */ PERF_SAMPLE_BRANCH_COND_SHIFT = 10, /* conditional branches */ PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT = 11, /* call/ret stack */ PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT = 12, /* indirect jumps */ PERF_SAMPLE_BRANCH_CALL_SHIFT = 13, /* direct call */ PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT = 14, /* no flags */ PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT = 15, /* no cycles */ PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT = 16, /* save branch type */ PERF_SAMPLE_BRANCH_MAX_SHIFT /* non-ABI */ }; enum perf_branch_sample_type { PERF_SAMPLE_BRANCH_USER = 1U << PERF_SAMPLE_BRANCH_USER_SHIFT, PERF_SAMPLE_BRANCH_KERNEL = 1U << PERF_SAMPLE_BRANCH_KERNEL_SHIFT, PERF_SAMPLE_BRANCH_HV = 1U << PERF_SAMPLE_BRANCH_HV_SHIFT, PERF_SAMPLE_BRANCH_ANY = 1U << PERF_SAMPLE_BRANCH_ANY_SHIFT, PERF_SAMPLE_BRANCH_ANY_CALL = 1U << PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT, PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT, PERF_SAMPLE_BRANCH_IND_CALL = 1U << PERF_SAMPLE_BRANCH_IND_CALL_SHIFT, PERF_SAMPLE_BRANCH_ABORT_TX = 1U << PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT, PERF_SAMPLE_BRANCH_IN_TX = 1U << PERF_SAMPLE_BRANCH_IN_TX_SHIFT, PERF_SAMPLE_BRANCH_NO_TX = 1U << PERF_SAMPLE_BRANCH_NO_TX_SHIFT, PERF_SAMPLE_BRANCH_COND = 1U << PERF_SAMPLE_BRANCH_COND_SHIFT, PERF_SAMPLE_BRANCH_CALL_STACK = 1U << PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT, PERF_SAMPLE_BRANCH_IND_JUMP = 1U << PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT, PERF_SAMPLE_BRANCH_CALL = 1U << PERF_SAMPLE_BRANCH_CALL_SHIFT, PERF_SAMPLE_BRANCH_NO_FLAGS = 1U << PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT, PERF_SAMPLE_BRANCH_NO_CYCLES = 1U << PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT, PERF_SAMPLE_BRANCH_TYPE_SAVE = 1U << PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT, PERF_SAMPLE_BRANCH_MAX = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT, }; /* * Common flow change classification */ enum { PERF_BR_UNKNOWN = 0, /* unknown */ PERF_BR_COND = 1, /* conditional */ PERF_BR_UNCOND = 2, /* unconditional */ PERF_BR_IND = 3, /* indirect */ PERF_BR_CALL = 4, /* function call */ PERF_BR_IND_CALL = 5, /* indirect function call */ PERF_BR_RET = 6, /* function return */ PERF_BR_SYSCALL = 7, /* syscall */ PERF_BR_SYSRET = 8, /* syscall return */ PERF_BR_COND_CALL = 9, /* conditional function call */ PERF_BR_COND_RET = 10, /* conditional function return */ PERF_BR_MAX, }; #define PERF_SAMPLE_BRANCH_PLM_ALL \ (PERF_SAMPLE_BRANCH_USER|\ PERF_SAMPLE_BRANCH_KERNEL|\ PERF_SAMPLE_BRANCH_HV) /* * Values to determine ABI of the registers dump. */ enum perf_sample_regs_abi { PERF_SAMPLE_REGS_ABI_NONE = 0, PERF_SAMPLE_REGS_ABI_32 = 1, PERF_SAMPLE_REGS_ABI_64 = 2, }; /* * Values for the memory transaction event qualifier, mostly for * abort events. Multiple bits can be set. */ enum { PERF_TXN_ELISION = (1 << 0), /* From elision */ PERF_TXN_TRANSACTION = (1 << 1), /* From transaction */ PERF_TXN_SYNC = (1 << 2), /* Instruction is related */ PERF_TXN_ASYNC = (1 << 3), /* Instruction not related */ PERF_TXN_RETRY = (1 << 4), /* Retry possible */ PERF_TXN_CONFLICT = (1 << 5), /* Conflict abort */ PERF_TXN_CAPACITY_WRITE = (1 << 6), /* Capacity write abort */ PERF_TXN_CAPACITY_READ = (1 << 7), /* Capacity read abort */ PERF_TXN_MAX = (1 << 8), /* non-ABI */ /* bits 32..63 are reserved for the abort code */ PERF_TXN_ABORT_MASK = (0xffffffffULL << 32), PERF_TXN_ABORT_SHIFT = 32, }; /* * The format of the data returned by read() on a perf event fd, * as specified by attr.read_format: * * struct read_format { * { u64 value; * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING * { u64 id; } && PERF_FORMAT_ID * } && !PERF_FORMAT_GROUP * * { u64 nr; * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING * { u64 value; * { u64 id; } && PERF_FORMAT_ID * } cntr[nr]; * } && PERF_FORMAT_GROUP * }; */ enum perf_event_read_format { PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0, PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1, PERF_FORMAT_ID = 1U << 2, PERF_FORMAT_GROUP = 1U << 3, PERF_FORMAT_MAX = 1U << 4, /* non-ABI */ }; #define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ #define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ #define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ /* add: sample_stack_user */ #define PERF_ATTR_SIZE_VER4 104 /* add: sample_regs_intr */ #define PERF_ATTR_SIZE_VER5 112 /* add: aux_watermark */ /* * Hardware event_id to monitor via a performance monitoring event: * * @sample_max_stack: Max number of frame pointers in a callchain, * should be < /proc/sys/kernel/perf_event_max_stack */ struct perf_event_attr { /* * Major type: hardware/software/tracepoint/etc. */ __u32 type; /* * Size of the attr structure, for fwd/bwd compat. */ __u32 size; /* * Type specific configuration information. */ __u64 config; union { __u64 sample_period; __u64 sample_freq; }; __u64 sample_type; __u64 read_format; __u64 disabled : 1, /* off by default */ inherit : 1, /* children inherit it */ pinned : 1, /* must always be on PMU */ exclusive : 1, /* only group on PMU */ exclude_user : 1, /* don't count user */ exclude_kernel : 1, /* ditto kernel */ exclude_hv : 1, /* ditto hypervisor */ exclude_idle : 1, /* don't count when idle */ mmap : 1, /* include mmap data */ comm : 1, /* include comm data */ freq : 1, /* use freq, not period */ inherit_stat : 1, /* per task counts */ enable_on_exec : 1, /* next exec enables */ task : 1, /* trace fork/exit */ watermark : 1, /* wakeup_watermark */ /* * precise_ip: * * 0 - SAMPLE_IP can have arbitrary skid * 1 - SAMPLE_IP must have constant skid * 2 - SAMPLE_IP requested to have 0 skid * 3 - SAMPLE_IP must have 0 skid * * See also PERF_RECORD_MISC_EXACT_IP */ precise_ip : 2, /* skid constraint */ mmap_data : 1, /* non-exec mmap data */ sample_id_all : 1, /* sample_type all events */ exclude_host : 1, /* don't count in host */ exclude_guest : 1, /* don't count in guest */ exclude_callchain_kernel : 1, /* exclude kernel callchains */ exclude_callchain_user : 1, /* exclude user callchains */ mmap2 : 1, /* include mmap with inode data */ comm_exec : 1, /* flag comm events that are due to an exec */ use_clockid : 1, /* use @clockid for time fields */ context_switch : 1, /* context switch data */ write_backward : 1, /* Write ring buffer from end to beginning */ namespaces : 1, /* include namespaces data */ __reserved_1 : 35; union { __u32 wakeup_events; /* wakeup every n events */ __u32 wakeup_watermark; /* bytes before wakeup */ }; __u32 bp_type; union { __u64 bp_addr; __u64 kprobe_func; /* for perf_kprobe */ __u64 uprobe_path; /* for perf_uprobe */ __u64 config1; /* extension of config */ }; union { __u64 bp_len; __u64 kprobe_addr; /* when kprobe_func == NULL */ __u64 probe_offset; /* for perf_[k,u]probe */ __u64 config2; /* extension of config1 */ }; __u64 branch_sample_type; /* enum perf_branch_sample_type */ /* * Defines set of user regs to dump on samples. * See asm/perf_regs.h for details. */ __u64 sample_regs_user; /* * Defines size of the user stack to dump on samples. */ __u32 sample_stack_user; __s32 clockid; /* * Defines set of regs to dump for each sample * state captured on: * - precise = 0: PMU interrupt * - precise > 0: sampled instruction * * See asm/perf_regs.h for details. */ __u64 sample_regs_intr; /* * Wakeup watermark for AUX area */ __u32 aux_watermark; __u16 sample_max_stack; __u16 __reserved_2; /* align to __u64 */ }; /* * Structure used by below PERF_EVENT_IOC_QUERY_BPF command * to query bpf programs attached to the same perf tracepoint * as the given perf event. */ struct perf_event_query_bpf { /* * The below ids array length */ __u32 ids_len; /* * Set by the kernel to indicate the number of * available programs */ __u32 prog_cnt; /* * User provided buffer to store program ids */ __u32 ids[0]; }; #define perf_flags(attr) (*(&(attr)->read_format + 1)) /* * Ioctls that can be done on a perf event fd: */ #define PERF_EVENT_IOC_ENABLE _IO ('$', 0) #define PERF_EVENT_IOC_DISABLE _IO ('$', 1) #define PERF_EVENT_IOC_REFRESH _IO ('$', 2) #define PERF_EVENT_IOC_RESET _IO ('$', 3) #define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64) #define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) #define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *) #define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *) #define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32) #define PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, __u32) #define PERF_EVENT_IOC_QUERY_BPF _IOWR('$', 10, struct perf_event_query_bpf *) #define PERF_EVENT_IOC_MODIFY_ATTRIBUTES _IOW('$', 11, struct perf_event_attr *) enum perf_event_ioc_flags { PERF_IOC_FLAG_GROUP = 1U << 0, }; /* * Structure of the page that can be mapped via mmap */ struct perf_event_mmap_page { __u32 version; /* version number of this structure */ __u32 compat_version; /* lowest version this is compat with */ /* * Bits needed to read the hw events in user-space. * * u32 seq, time_mult, time_shift, index, width; * u64 count, enabled, running; * u64 cyc, time_offset; * s64 pmc = 0; * * do { * seq = pc->lock; * barrier() * * enabled = pc->time_enabled; * running = pc->time_running; * * if (pc->cap_usr_time && enabled != running) { * cyc = rdtsc(); * time_offset = pc->time_offset; * time_mult = pc->time_mult; * time_shift = pc->time_shift; * } * * index = pc->index; * count = pc->offset; * if (pc->cap_user_rdpmc && index) { * width = pc->pmc_width; * pmc = rdpmc(index - 1); * } * * barrier(); * } while (pc->lock != seq); * * NOTE: for obvious reason this only works on self-monitoring * processes. */ __u32 lock; /* seqlock for synchronization */ __u32 index; /* hardware event identifier */ __s64 offset; /* add to hardware event value */ __u64 time_enabled; /* time event active */ __u64 time_running; /* time event on cpu */ union { __u64 capabilities; struct { __u64 cap_bit0 : 1, /* Always 0, deprecated, see commit 860f085b74e9 */ cap_bit0_is_deprecated : 1, /* Always 1, signals that bit 0 is zero */ cap_user_rdpmc : 1, /* The RDPMC instruction can be used to read counts */ cap_user_time : 1, /* The time_* fields are used */ cap_user_time_zero : 1, /* The time_zero field is used */ cap_____res : 59; }; }; /* * If cap_user_rdpmc this field provides the bit-width of the value * read using the rdpmc() or equivalent instruction. This can be used * to sign extend the result like: * * pmc <<= 64 - width; * pmc >>= 64 - width; // signed shift right * count += pmc; */ __u16 pmc_width; /* * If cap_usr_time the below fields can be used to compute the time * delta since time_enabled (in ns) using rdtsc or similar. * * u64 quot, rem; * u64 delta; * * quot = (cyc >> time_shift); * rem = cyc & (((u64)1 << time_shift) - 1); * delta = time_offset + quot * time_mult + * ((rem * time_mult) >> time_shift); * * Where time_offset,time_mult,time_shift and cyc are read in the * seqcount loop described above. This delta can then be added to * enabled and possible running (if index), improving the scaling: * * enabled += delta; * if (index) * running += delta; * * quot = count / running; * rem = count % running; * count = quot * enabled + (rem * enabled) / running; */ __u16 time_shift; __u32 time_mult; __u64 time_offset; /* * If cap_usr_time_zero, the hardware clock (e.g. TSC) can be calculated * from sample timestamps. * * time = timestamp - time_zero; * quot = time / time_mult; * rem = time % time_mult; * cyc = (quot << time_shift) + (rem << time_shift) / time_mult; * * And vice versa: * * quot = cyc >> time_shift; * rem = cyc & (((u64)1 << time_shift) - 1); * timestamp = time_zero + quot * time_mult + * ((rem * time_mult) >> time_shift); */ __u64 time_zero; __u32 size; /* Header size up to __reserved[] fields. */ /* * Hole for extension of the self monitor capabilities */ __u8 __reserved[118*8+4]; /* align to 1k. */ /* * Control data for the mmap() data buffer. * * User-space reading the @data_head value should issue an smp_rmb(), * after reading this value. * * When the mapping is PROT_WRITE the @data_tail value should be * written by userspace to reflect the last read data, after issueing * an smp_mb() to separate the data read from the ->data_tail store. * In this case the kernel will not over-write unread data. * * See perf_output_put_handle() for the data ordering. * * data_{offset,size} indicate the location and size of the perf record * buffer within the mmapped area. */ __u64 data_head; /* head in the data section */ __u64 data_tail; /* user-space written tail */ __u64 data_offset; /* where the buffer starts */ __u64 data_size; /* data buffer size */ /* * AUX area is defined by aux_{offset,size} fields that should be set * by the userspace, so that * * aux_offset >= data_offset + data_size * * prior to mmap()ing it. Size of the mmap()ed area should be aux_size. * * Ring buffer pointers aux_{head,tail} have the same semantics as * data_{head,tail} and same ordering rules apply. */ __u64 aux_head; __u64 aux_tail; __u64 aux_offset; __u64 aux_size; }; #define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0) #define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0) #define PERF_RECORD_MISC_KERNEL (1 << 0) #define PERF_RECORD_MISC_USER (2 << 0) #define PERF_RECORD_MISC_HYPERVISOR (3 << 0) #define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0) #define PERF_RECORD_MISC_GUEST_USER (5 << 0) /* * Indicates that /proc/PID/maps parsing are truncated by time out. */ #define PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT (1 << 12) /* * Following PERF_RECORD_MISC_* are used on different * events, so can reuse the same bit position: * * PERF_RECORD_MISC_MMAP_DATA - PERF_RECORD_MMAP* events * PERF_RECORD_MISC_COMM_EXEC - PERF_RECORD_COMM event * PERF_RECORD_MISC_SWITCH_OUT - PERF_RECORD_SWITCH* events */ #define PERF_RECORD_MISC_MMAP_DATA (1 << 13) #define PERF_RECORD_MISC_COMM_EXEC (1 << 13) #define PERF_RECORD_MISC_SWITCH_OUT (1 << 13) /* * These PERF_RECORD_MISC_* flags below are safely reused * for the following events: * * PERF_RECORD_MISC_EXACT_IP - PERF_RECORD_SAMPLE of precise events * PERF_RECORD_MISC_SWITCH_OUT_PREEMPT - PERF_RECORD_SWITCH* events * * * PERF_RECORD_MISC_EXACT_IP: * Indicates that the content of PERF_SAMPLE_IP points to * the actual instruction that triggered the event. See also * perf_event_attr::precise_ip. * * PERF_RECORD_MISC_SWITCH_OUT_PREEMPT: * Indicates that thread was preempted in TASK_RUNNING state. */ #define PERF_RECORD_MISC_EXACT_IP (1 << 14) #define PERF_RECORD_MISC_SWITCH_OUT_PREEMPT (1 << 14) /* * Reserve the last bit to indicate some extended misc field */ #define PERF_RECORD_MISC_EXT_RESERVED (1 << 15) struct perf_event_header { __u32 type; __u16 misc; __u16 size; }; struct perf_ns_link_info { __u64 dev; __u64 ino; }; enum { NET_NS_INDEX = 0, UTS_NS_INDEX = 1, IPC_NS_INDEX = 2, PID_NS_INDEX = 3, USER_NS_INDEX = 4, MNT_NS_INDEX = 5, CGROUP_NS_INDEX = 6, NR_NAMESPACES, /* number of available namespaces */ }; enum perf_event_type { /* * If perf_event_attr.sample_id_all is set then all event types will * have the sample_type selected fields related to where/when * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU, * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed * just after the perf_event_header and the fields already present for * the existing fields, i.e. at the end of the payload. That way a newer * perf.data file will be supported by older perf tools, with these new * optional fields being ignored. * * struct sample_id { * { u32 pid, tid; } && PERF_SAMPLE_TID * { u64 time; } && PERF_SAMPLE_TIME * { u64 id; } && PERF_SAMPLE_ID * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID * { u32 cpu, res; } && PERF_SAMPLE_CPU * { u64 id; } && PERF_SAMPLE_IDENTIFIER * } && perf_event_attr::sample_id_all * * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed * relative to header.size. */ /* * The MMAP events record the PROT_EXEC mappings so that we can * correlate userspace IPs to code. They have the following structure: * * struct { * struct perf_event_header header; * * u32 pid, tid; * u64 addr; * u64 len; * u64 pgoff; * char filename[]; * struct sample_id sample_id; * }; */ PERF_RECORD_MMAP = 1, /* * struct { * struct perf_event_header header; * u64 id; * u64 lost; * struct sample_id sample_id; * }; */ PERF_RECORD_LOST = 2, /* * struct { * struct perf_event_header header; * * u32 pid, tid; * char comm[]; * struct sample_id sample_id; * }; */ PERF_RECORD_COMM = 3, /* * struct { * struct perf_event_header header; * u32 pid, ppid; * u32 tid, ptid; * u64 time; * struct sample_id sample_id; * }; */ PERF_RECORD_EXIT = 4, /* * struct { * struct perf_event_header header; * u64 time; * u64 id; * u64 stream_id; * struct sample_id sample_id; * }; */ PERF_RECORD_THROTTLE = 5, PERF_RECORD_UNTHROTTLE = 6, /* * struct { * struct perf_event_header header; * u32 pid, ppid; * u32 tid, ptid; * u64 time; * struct sample_id sample_id; * }; */ PERF_RECORD_FORK = 7, /* * struct { * struct perf_event_header header; * u32 pid, tid; * * struct read_format values; * struct sample_id sample_id; * }; */ PERF_RECORD_READ = 8, /* * struct { * struct perf_event_header header; * * # * # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. * # The advantage of PERF_SAMPLE_IDENTIFIER is that its position * # is fixed relative to header. * # * * { u64 id; } && PERF_SAMPLE_IDENTIFIER * { u64 ip; } && PERF_SAMPLE_IP * { u32 pid, tid; } && PERF_SAMPLE_TID * { u64 time; } && PERF_SAMPLE_TIME * { u64 addr; } && PERF_SAMPLE_ADDR * { u64 id; } && PERF_SAMPLE_ID * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID * { u32 cpu, res; } && PERF_SAMPLE_CPU * { u64 period; } && PERF_SAMPLE_PERIOD * * { struct read_format values; } && PERF_SAMPLE_READ * * { u64 nr, * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN * * # * # The RAW record below is opaque data wrt the ABI * # * # That is, the ABI doesn't make any promises wrt to * # the stability of its content, it may vary depending * # on event, hardware, kernel version and phase of * # the moon. * # * # In other words, PERF_SAMPLE_RAW contents are not an ABI. * # * * { u32 size; * char data[size];}&& PERF_SAMPLE_RAW * * { u64 nr; * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK * * { u64 abi; # enum perf_sample_regs_abi * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER * * { u64 size; * char data[size]; * u64 dyn_size; } && PERF_SAMPLE_STACK_USER * * { u64 weight; } && PERF_SAMPLE_WEIGHT * { u64 data_src; } && PERF_SAMPLE_DATA_SRC * { u64 transaction; } && PERF_SAMPLE_TRANSACTION * { u64 abi; # enum perf_sample_regs_abi * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR * { u64 phys_addr;} && PERF_SAMPLE_PHYS_ADDR * }; */ PERF_RECORD_SAMPLE = 9, /* * The MMAP2 records are an augmented version of MMAP, they add * maj, min, ino numbers to be used to uniquely identify each mapping * * struct { * struct perf_event_header header; * * u32 pid, tid; * u64 addr; * u64 len; * u64 pgoff; * u32 maj; * u32 min; * u64 ino; * u64 ino_generation; * u32 prot, flags; * char filename[]; * struct sample_id sample_id; * }; */ PERF_RECORD_MMAP2 = 10, /* * Records that new data landed in the AUX buffer part. * * struct { * struct perf_event_header header; * * u64 aux_offset; * u64 aux_size; * u64 flags; * struct sample_id sample_id; * }; */ PERF_RECORD_AUX = 11, /* * Indicates that instruction trace has started * * struct { * struct perf_event_header header; * u32 pid; * u32 tid; * struct sample_id sample_id; * }; */ PERF_RECORD_ITRACE_START = 12, /* * Records the dropped/lost sample number. * * struct { * struct perf_event_header header; * * u64 lost; * struct sample_id sample_id; * }; */ PERF_RECORD_LOST_SAMPLES = 13, /* * Records a context switch in or out (flagged by * PERF_RECORD_MISC_SWITCH_OUT). See also * PERF_RECORD_SWITCH_CPU_WIDE. * * struct { * struct perf_event_header header; * struct sample_id sample_id; * }; */ PERF_RECORD_SWITCH = 14, /* * CPU-wide version of PERF_RECORD_SWITCH with next_prev_pid and * next_prev_tid that are the next (switching out) or previous * (switching in) pid/tid. * * struct { * struct perf_event_header header; * u32 next_prev_pid; * u32 next_prev_tid; * struct sample_id sample_id; * }; */ PERF_RECORD_SWITCH_CPU_WIDE = 15, /* * struct { * struct perf_event_header header; * u32 pid; * u32 tid; * u64 nr_namespaces; * { u64 dev, inode; } [nr_namespaces]; * struct sample_id sample_id; * }; */ PERF_RECORD_NAMESPACES = 16, PERF_RECORD_MAX, /* non-ABI */ }; #define PERF_MAX_STACK_DEPTH 127 #define PERF_MAX_CONTEXTS_PER_STACK 8 enum perf_callchain_context { PERF_CONTEXT_HV = (__u64)-32, PERF_CONTEXT_KERNEL = (__u64)-128, PERF_CONTEXT_USER = (__u64)-512, PERF_CONTEXT_GUEST = (__u64)-2048, PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176, PERF_CONTEXT_GUEST_USER = (__u64)-2560, PERF_CONTEXT_MAX = (__u64)-4095, }; /** * PERF_RECORD_AUX::flags bits */ #define PERF_AUX_FLAG_TRUNCATED 0x01 /* record was truncated to fit */ #define PERF_AUX_FLAG_OVERWRITE 0x02 /* snapshot from overwrite mode */ #define PERF_AUX_FLAG_PARTIAL 0x04 /* record contains gaps */ #define PERF_AUX_FLAG_COLLISION 0x08 /* sample collided with another */ #define PERF_FLAG_FD_NO_GROUP (1UL << 0) #define PERF_FLAG_FD_OUTPUT (1UL << 1) #define PERF_FLAG_PID_CGROUP (1UL << 2) /* pid=cgroup id, per-cpu mode only */ #define PERF_FLAG_FD_CLOEXEC (1UL << 3) /* O_CLOEXEC */ #if defined(__LITTLE_ENDIAN_BITFIELD) union perf_mem_data_src { __u64 val; struct { __u64 mem_op:5, /* type of opcode */ mem_lvl:14, /* memory hierarchy level */ mem_snoop:5, /* snoop mode */ mem_lock:2, /* lock instr */ mem_dtlb:7, /* tlb access */ mem_lvl_num:4, /* memory hierarchy level number */ mem_remote:1, /* remote */ mem_snoopx:2, /* snoop mode, ext */ mem_rsvd:24; }; }; #elif defined(__BIG_ENDIAN_BITFIELD) union perf_mem_data_src { __u64 val; struct { __u64 mem_rsvd:24, mem_snoopx:2, /* snoop mode, ext */ mem_remote:1, /* remote */ mem_lvl_num:4, /* memory hierarchy level number */ mem_dtlb:7, /* tlb access */ mem_lock:2, /* lock instr */ mem_snoop:5, /* snoop mode */ mem_lvl:14, /* memory hierarchy level */ mem_op:5; /* type of opcode */ }; }; #else #error "Unknown endianness" #endif /* type of opcode (load/store/prefetch,code) */ #define PERF_MEM_OP_NA 0x01 /* not available */ #define PERF_MEM_OP_LOAD 0x02 /* load instruction */ #define PERF_MEM_OP_STORE 0x04 /* store instruction */ #define PERF_MEM_OP_PFETCH 0x08 /* prefetch */ #define PERF_MEM_OP_EXEC 0x10 /* code (execution) */ #define PERF_MEM_OP_SHIFT 0 /* memory hierarchy (memory level, hit or miss) */ #define PERF_MEM_LVL_NA 0x01 /* not available */ #define PERF_MEM_LVL_HIT 0x02 /* hit level */ #define PERF_MEM_LVL_MISS 0x04 /* miss level */ #define PERF_MEM_LVL_L1 0x08 /* L1 */ #define PERF_MEM_LVL_LFB 0x10 /* Line Fill Buffer */ #define PERF_MEM_LVL_L2 0x20 /* L2 */ #define PERF_MEM_LVL_L3 0x40 /* L3 */ #define PERF_MEM_LVL_LOC_RAM 0x80 /* Local DRAM */ #define PERF_MEM_LVL_REM_RAM1 0x100 /* Remote DRAM (1 hop) */ #define PERF_MEM_LVL_REM_RAM2 0x200 /* Remote DRAM (2 hops) */ #define PERF_MEM_LVL_REM_CCE1 0x400 /* Remote Cache (1 hop) */ #define PERF_MEM_LVL_REM_CCE2 0x800 /* Remote Cache (2 hops) */ #define PERF_MEM_LVL_IO 0x1000 /* I/O memory */ #define PERF_MEM_LVL_UNC 0x2000 /* Uncached memory */ #define PERF_MEM_LVL_SHIFT 5 #define PERF_MEM_REMOTE_REMOTE 0x01 /* Remote */ #define PERF_MEM_REMOTE_SHIFT 37 #define PERF_MEM_LVLNUM_L1 0x01 /* L1 */ #define PERF_MEM_LVLNUM_L2 0x02 /* L2 */ #define PERF_MEM_LVLNUM_L3 0x03 /* L3 */ #define PERF_MEM_LVLNUM_L4 0x04 /* L4 */ /* 5-0xa available */ #define PERF_MEM_LVLNUM_ANY_CACHE 0x0b /* Any cache */ #define PERF_MEM_LVLNUM_LFB 0x0c /* LFB */ #define PERF_MEM_LVLNUM_RAM 0x0d /* RAM */ #define PERF_MEM_LVLNUM_PMEM 0x0e /* PMEM */ #define PERF_MEM_LVLNUM_NA 0x0f /* N/A */ #define PERF_MEM_LVLNUM_SHIFT 33 /* snoop mode */ #define PERF_MEM_SNOOP_NA 0x01 /* not available */ #define PERF_MEM_SNOOP_NONE 0x02 /* no snoop */ #define PERF_MEM_SNOOP_HIT 0x04 /* snoop hit */ #define PERF_MEM_SNOOP_MISS 0x08 /* snoop miss */ #define PERF_MEM_SNOOP_HITM 0x10 /* snoop hit modified */ #define PERF_MEM_SNOOP_SHIFT 19 #define PERF_MEM_SNOOPX_FWD 0x01 /* forward */ /* 1 free */ #define PERF_MEM_SNOOPX_SHIFT 37 /* locked instruction */ #define PERF_MEM_LOCK_NA 0x01 /* not available */ #define PERF_MEM_LOCK_LOCKED 0x02 /* locked transaction */ #define PERF_MEM_LOCK_SHIFT 24 /* TLB access */ #define PERF_MEM_TLB_NA 0x01 /* not available */ #define PERF_MEM_TLB_HIT 0x02 /* hit level */ #define PERF_MEM_TLB_MISS 0x04 /* miss level */ #define PERF_MEM_TLB_L1 0x08 /* L1 */ #define PERF_MEM_TLB_L2 0x10 /* L2 */ #define PERF_MEM_TLB_WK 0x20 /* Hardware Walker*/ #define PERF_MEM_TLB_OS 0x40 /* OS fault handler */ #define PERF_MEM_TLB_SHIFT 26 #define PERF_MEM_S(a, s) \ (((__u64)PERF_MEM_##a##_##s) << PERF_MEM_##a##_SHIFT) /* * single taken branch record layout: * * from: source instruction (may not always be a branch insn) * to: branch target * mispred: branch target was mispredicted * predicted: branch target was predicted * * support for mispred, predicted is optional. In case it * is not supported mispred = predicted = 0. * * in_tx: running in a hardware transaction * abort: aborting a hardware transaction * cycles: cycles from last branch (or 0 if not supported) * type: branch type */ struct perf_branch_entry { __u64 from; __u64 to; __u64 mispred:1, /* target mispredicted */ predicted:1,/* target predicted */ in_tx:1, /* in transaction */ abort:1, /* transaction abort */ cycles:16, /* cycle count to last branch */ type:4, /* branch type */ reserved:40; }; #endif /* _UAPI_LINUX_PERF_EVENT_H */ sysdig-0.26.4/userspace/libscap/doxygen/000077500000000000000000000000001352731327100201755ustar00rootroot00000000000000sysdig-0.26.4/userspace/libscap/doxygen/conf.dox000066400000000000000000000176061352731327100216500ustar00rootroot00000000000000# Doxyfile 1.3.7 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- PROJECT_NAME = libscap PROJECT_NUMBER = 0.1.0.0 OUTPUT_DIRECTORY = CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English USE_WINDOWS_ENCODING = YES BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO DETAILS_AT_TOP = NO INHERIT_DOCS = YES DISTRIBUTE_GROUP_DOC = NO TAB_SIZE = 4 ALIASES = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = NO GENERATE_TESTLIST = NO GENERATE_BUGLIST = NO GENERATE_DEPRECATEDLIST= NO ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = NO #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = ../scap.h FILE_PATTERNS = RECURSIVE = NO EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = header.html HTML_FOOTER = footer.html HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = YES CHM_FILE = HHC_LOCATION = GENERATE_CHI = YES BINARY_TOC = YES TOC_EXPAND = YES DISABLE_INDEX = YES ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = YES TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = NO USE_PDFLATEX = NO LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO CLASS_GRAPH = YES COLLABORATION_GRAPH = YES UML_LOOK = NO TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO GRAPHICAL_HIERARCHY = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = MAX_DOT_GRAPH_WIDTH = 1024 MAX_DOT_GRAPH_HEIGHT = 1024 MAX_DOT_GRAPH_DEPTH = 0 GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = NO sysdig-0.26.4/userspace/libscap/doxygen/footer.html000066400000000000000000000000301352731327100223520ustar00rootroot00000000000000 sysdig-0.26.4/userspace/libscap/doxygen/header.html000066400000000000000000000026111352731327100223130ustar00rootroot00000000000000--- layout: default title: sysdig | libscap ---

$projectname $projectnumber

(Generated by Doxygen)

$projectbrief

$projectbrief
$searchbox
sysdig-0.26.4/userspace/libscap/examples/000077500000000000000000000000001352731327100203365ustar00rootroot00000000000000sysdig-0.26.4/userspace/libscap/examples/01-open/000077500000000000000000000000001352731327100215155ustar00rootroot00000000000000sysdig-0.26.4/userspace/libscap/examples/01-open/CMakeLists.txt000066400000000000000000000002201352731327100242470ustar00rootroot00000000000000include_directories("../../../common") include_directories("../../") add_executable(scap-open test.c) target_link_libraries(scap-open scap) sysdig-0.26.4/userspace/libscap/examples/01-open/test.c000066400000000000000000000042131352731327100226400ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include uint64_t g_nevts = 0; scap_t* g_h = NULL; static void signal_callback(int signal) { scap_stats s; printf("events captured: %" PRIu64 "\n", g_nevts); scap_get_stats(g_h, &s); printf("seen by driver: %" PRIu64 "\n", s.n_evts); printf("Number of dropped events: %" PRIu64 "\n", s.n_drops); printf("Number of dropped events caused by full buffer: %" PRIu64 "\n", s.n_drops_buffer); printf("Number of dropped events caused by invalid memory access: %" PRIu64 "\n", s.n_drops_pf); printf("Number of dropped events caused by an invalid condition in the kernel instrumentation: %" PRIu64 "\n", s.n_drops_bug); printf("Number of preemptions: %" PRIu64 "\n", s.n_preemptions); printf("Number of events skipped due to the tid being in a set of suppressed tids: %" PRIu64 "\n", s.n_suppressed); printf("Number of threads currently being suppressed: %" PRIu64 "\n", s.n_tids_suppressed); exit(0); } int main(int argc, char** argv) { char error[SCAP_LASTERR_SIZE]; int32_t res; scap_evt* ev; uint16_t cpuid; if(signal(SIGINT, signal_callback) == SIG_ERR) { fprintf(stderr, "An error occurred while setting SIGINT signal handler.\n"); return -1; } g_h = scap_open_live(error, &res); if(g_h == NULL) { fprintf(stderr, "%s (%d)\n", error, res); return -1; } while(1) { res = scap_next(g_h, &ev, &cpuid); if(res > 0) { fprintf(stderr, "%s\n", scap_getlasterr(g_h)); scap_close(g_h); return -1; } if(res != SCAP_TIMEOUT) { g_nevts++; } } scap_close(g_h); return 0; } sysdig-0.26.4/userspace/libscap/examples/02-validatebuffer/000077500000000000000000000000001352731327100235405ustar00rootroot00000000000000sysdig-0.26.4/userspace/libscap/examples/02-validatebuffer/CMakeLists.txt000066400000000000000000000002431352731327100262770ustar00rootroot00000000000000include_directories("../../../common") include_directories("../..") add_executable(scap-validatebuffer test.c) target_link_libraries(scap-validatebuffer scap) sysdig-0.26.4/userspace/libscap/examples/02-validatebuffer/test.c000066400000000000000000000144141352731327100246670ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include "../../../../driver/ppm_events_public.h" extern const struct ppm_event_info g_event_info[]; size_t g_get_event_size(enum ppm_event_type event_type, uint16_t* lens) { uint32_t j; int32_t res = 0; for(j = 0; j < g_event_info[event_type].nparams; j++) { res += lens[j]; } #ifdef PPM_ENABLE_SENTINEL return res + j * sizeof(uint16_t) + sizeof(struct ppm_evt_hdr) + sizeof(uint32_t); #else return res + j * sizeof(uint16_t) + sizeof(struct ppm_evt_hdr); #endif } int32_t g_check_integrity(uint32_t* cur_event, char* copy_buffer, int buf_len, OUT uint32_t* nevents) { uint32_t offset = 0; *nevents = 0; while(buf_len) { #ifdef PPM_ENABLE_SENTINEL uint32_t sentinel_begin; uint32_t sentinel_end; #endif struct ppm_evt_hdr* hdr; size_t event_size; if(buf_len < sizeof(struct ppm_evt_hdr)) { fprintf(stderr, "Error: event not on buffer boundary, offset %x, data to read %d\n", offset, buf_len); return SCAP_FAILURE; } hdr = (struct ppm_evt_hdr*)(copy_buffer + offset); uint16_t type = hdr->type; if(buf_len < sizeof(struct ppm_evt_hdr) + g_event_info[type].nparams * sizeof(uint16_t)) { fprintf(stderr, "Error: event not on buffer boundary, offset %x, data to read %d\n", offset, buf_len); return SCAP_FAILURE; } event_size = g_get_event_size(hdr->type, (uint16_t*)(copy_buffer + offset + sizeof(struct ppm_evt_hdr))); if(event_size == -1) { fprintf(stderr, "Error: unrecognized event %u, cnt %u, offset %x\n", (uint32_t)(hdr->type), (*cur_event == -1)?0:*cur_event, offset); return SCAP_FAILURE; } if(event_size < sizeof(struct ppm_evt_hdr) + g_event_info[hdr->type].nparams * sizeof(uint16_t)) { fprintf(stderr, "Error: event size too short %u, cnt %u, offset %x\n", (unsigned int)event_size, (*cur_event == -1)?0:*cur_event, offset); return SCAP_FAILURE; } #ifdef PPM_ENABLE_SENTINEL sentinel_begin = ((struct ppm_evt_hdr*)(copy_buffer + offset))->sentinel_begin; sentinel_end = *(uint32_t*)(copy_buffer + offset + event_size - sizeof(uint32_t)); if(sentinel_begin != sentinel_end) { fprintf(stderr, "Error: sentinel begin %d, sentinel end %d, evt_type %u, evt_size %zu, cnt %u, offset %x, remaining %u\n", sentinel_begin, sentinel_end, (uint32_t)hdr->type, event_size, (*cur_event == -1)?0:*cur_event, offset, buf_len); return SCAP_FAILURE; } if(*cur_event == -1) { *cur_event = sentinel_begin; } if(sentinel_begin != *cur_event) { fprintf(stderr, "Error1: sentinel begin %d, sentinel end %d, cnt %u, offset %x, remaining %u\n", sentinel_begin, sentinel_end, *cur_event, offset, buf_len); return SCAP_FAILURE; } #endif buf_len -= event_size; offset += event_size; ++(*nevents); ++(*cur_event); } return 0; } int main() { uint32_t j; char error[SCAP_LASTERR_SIZE]; int32_t ret; char* buf; uint32_t buflen; uint32_t cur_evts[256]; int32_t ndevs; uint32_t nloops = 0; uint64_t totbytes = 0; uint64_t totevents = 0; uint64_t devicebytes[256]; uint64_t deviceevents[256]; uint64_t oldtotbytes = 0; uint64_t oldtotevents = 0; uint64_t olddevicebytes[256]; uint64_t olddeviceevents[256]; /* unsigned long new_mask = 1 << (1); sched_setaffinity(0, sizeof(unsigned long), &new_mask); */ scap_t* h = scap_open_live(error, &ret); if(h == NULL) { fprintf(stderr, "%s (%d)\n", error, ret); return ret; } ndevs = scap_get_ndevs(h); if(ndevs > sizeof(cur_evts)/sizeof(cur_evts[0])) { fprintf(stderr, "too many devices %u\n", ndevs); return -1; } for(j = 0; j < ndevs; j++) { devicebytes[j] = 0; deviceevents[j] = 0; olddevicebytes[j] = 0; olddeviceevents[j] = 0; } while(1) { for(j = 0; j < ndevs; j++) { uint32_t nevents; ret = scap_readbuf(h, j, &buf, &buflen); if(ret != SCAP_SUCCESS) { fprintf(stderr, "%s\n", scap_getlasterr(h)); scap_close(h); return -1; } cur_evts[j] = -1; if(g_check_integrity(&(cur_evts[j]), buf, buflen, &nevents) != SCAP_SUCCESS) { fprintf(stderr, "Integrity check failure at event %u.\nDumping buffer to dump.bin\n", (cur_evts[j] == -1)?0:cur_evts[j]); FILE* f; f= fopen("dump.bin", "w"); fwrite(buf, buflen, 1, f); fclose(f); exit(-1); } totbytes += buflen; totevents += nevents; devicebytes[j] += buflen; deviceevents[j] += nevents; if(nloops == 1000) { printf(" %u)bps:%" PRIu64 " totbytes:%" PRIu64 " - evts/s:%" PRIu64 " totevs:%" PRIu64 " \n", j, (devicebytes[j] - olddevicebytes[j]), devicebytes[j], (deviceevents[j] - olddeviceevents[j]), deviceevents[j]); olddevicebytes[j] = devicebytes[j]; olddeviceevents[j] = deviceevents[j]; } } // // XXX this should check the buffer sizes and sleep only if they are all below a certain // threshold. // usleep(1000); if(nloops == 1000) { scap_stats stats; if(scap_get_stats(h, &stats) != SCAP_SUCCESS) { fprintf(stderr, "%s\n", scap_getlasterr(h)); scap_close(h); return -1; } printf("bps:%" PRIu64 " totbytes:%" PRIu64 " - evts/s:%" PRIu64 " totevs:%" PRIu64 " drops:%" PRIu64 "\n", totbytes - oldtotbytes, totbytes, totevents - oldtotevents, totevents, stats.n_drops); oldtotbytes = totbytes; oldtotevents = totevents; nloops = 0; } nloops++; } scap_close(h); return 0; } sysdig-0.26.4/userspace/libscap/scap-int.h000066400000000000000000000261301352731327100204110ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ //////////////////////////////////////////////////////////////////////////// // Private definitions for the scap library //////////////////////////////////////////////////////////////////////////// #include "settings.h" #ifdef __cplusplus extern "C" { #endif #ifdef CYGWING_AGENT typedef struct wh_t wh_t; #endif #ifdef _WIN32 #define _CRTDBG_MAP_ALLOC #include #include #endif #include #ifdef USE_ZLIB #include #else #define gzFile FILE* #define gzflush(X, Y) fflush(X) #define gzopen fopen #define gzdopen(fd, mode) stdout #define gzclose fclose #define gzoffset ftell #define gzwrite(F, B, S) fwrite(B, 1, S, F) #define gzread(F, B, S) fread(B, 1, S, F) #define gzseek fseek #endif // // Read buffer timeout constants // #define BUFFER_EMPTY_WAIT_TIME_US_START 500 #define BUFFER_EMPTY_WAIT_TIME_US_MAX (30 * 1000) #define BUFFER_EMPTY_THRESHOLD_B 20000 // // Process flags // #define PF_CLONING 1 // // ebpf defs // #define BPF_PROGS_MAX 128 #define BPF_MAPS_MAX 32 // // The device descriptor // typedef struct scap_device { int m_fd; char* m_buffer; uint32_t m_lastreadsize; char* m_sn_next_event; // Pointer to the next event available for scap_next uint32_t m_sn_len; // Number of bytes available in the buffer pointed by m_sn_next_event union { // Anonymous struct with ppm stuff struct { struct ppm_ring_buffer_info* m_bufinfo; }; // Anonymous struct with bpf stuff struct { uint64_t m_evt_lost; }; }; }scap_device; typedef struct scap_tid { uint64_t tid; UT_hash_handle hh; ///< makes this structure hashable } scap_tid; // // The open instance handle // struct scap { scap_mode_t m_mode; scap_device* m_devs; uint32_t m_ndevs; #ifdef USE_ZLIB gzFile m_file; #else FILE* m_file; #endif char* m_file_evt_buf; uint32_t m_last_evt_dump_flags; char m_lasterr[SCAP_LASTERR_SIZE]; // Used for scap_strerror char m_strerror_buf[SCAP_LASTERR_SIZE]; scap_threadinfo* m_proclist; scap_mountinfo* m_dev_list; scap_threadinfo m_fake_kernel_proc; uint64_t m_evtcnt; scap_addrlist* m_addrlist; scap_machine_info m_machine_info; scap_userlist* m_userlist; uint64_t m_buffer_empty_wait_time_us; proc_entry_callback m_proc_callback; void* m_proc_callback_context; struct ppm_proclist_info* m_driver_procinfo; bool refresh_proc_table_when_saving; uint32_t m_fd_lookup_limit; uint64_t m_unexpected_block_readsize; uint32_t m_ncpus; // Abstraction layer for windows #ifdef CYGWING_AGENT wh_t* m_whh; #endif bool m_bpf; // Anonymous struct with bpf stuff struct { int m_bpf_prog_fds[BPF_PROGS_MAX]; int m_bpf_prog_cnt; bool m_bpf_fillers[BPF_PROGS_MAX]; int m_bpf_event_fd[BPF_PROGS_MAX]; int m_bpf_map_fds[BPF_MAPS_MAX]; int m_bpf_prog_array_map_idx; }; // The set of process names that are suppressed char **m_suppressed_comms; uint32_t m_num_suppressed_comms; // The active set of threads that are suppressed scap_tid *m_suppressed_tids; // The number of events that were skipped due to the comm // matching an entry in m_suppressed_comms. uint64_t m_num_suppressed_evts; }; typedef enum ppm_dumper_type { DT_FILE = 0, DT_MEM = 1, }ppm_dumper_type; struct scap_dumper { gzFile m_f; ppm_dumper_type m_type; uint8_t* m_targetbuf; uint8_t* m_targetbufcurpos; uint8_t* m_targetbufend; }; struct scap_ns_socket_list { int64_t net_ns; scap_fdinfo* sockets; UT_hash_handle hh; }; // // Misc stuff // #define MEMBER_SIZE(type, member) sizeof(((type *)0)->member) #define FILE_READ_BUF_SIZE 65536 // // Internal library functions // // Read the full event buffer for the given processor int32_t scap_readbuf(scap_t* handle, uint32_t proc, OUT char** buf, OUT uint32_t* len); // Read a single thread info from /proc int32_t scap_proc_read_thread(scap_t* handle, char* procdirname, uint64_t tid, struct scap_threadinfo** pi, char *error, bool scan_sockets); // Scan a directory containing process information int32_t scap_proc_scan_proc_dir(scap_t* handle, char* procdirname, char *error); // Remove an entry from the process list by parsing a PPME_PROC_EXIT event // void scap_proc_schedule_removal(scap_t* handle, scap_evt* e); // Remove the process that was scheduled for deletion for this handle // void scap_proc_remove_scheduled(scap_t* handle); // Free the process table void scap_proc_free_table(scap_t* handle); // Copy the fd table of a process into the one of another process // int32_t scap_proc_copy_fd_table(scap_t* handle, scap_threadinfo* dst, scap_threadinfo* src); // Internal helper function to output the process table to screen void scap_proc_print_info(scap_t *handle, scap_threadinfo* pi); void scap_proc_print_table(scap_t* handle); // Free all the state related to a process and delete it from the fd table void scap_proc_delete(scap_t* handle, scap_threadinfo* proc); // Internal helper function to output the fd table of a process void scap_fd_print_table(scap_t *handle, scap_threadinfo* pi); // Internal helper function to output an fd table void scap_fd_print_fd_table(scap_t *handle, scap_fdinfo* fds); // Given an event, get the info entry for the process that generated it. // NOTE: this is different from scap_event_getprocinfo() because it returns the full event information // struct scap_threadinfo* scap_proc_get_from_event(scap_t* handle, scap_evt* e); // Return the process info entry given a tid // Free an fd table and set it to NULL when done void scap_fd_free_table(scap_t* handle, scap_fdinfo** fds); void scap_fd_free_ns_sockets_list(scap_t* handle, struct scap_ns_socket_list** sockets); // Free a process' fd table void scap_fd_free_proc_fd_table(scap_t* handle, scap_threadinfo* pi); // Convert an fd entry's info into a string int32_t scap_fd_info_to_string(scap_t *handle, scap_fdinfo* fdi, OUT char* str, uint32_t strlen); // Calculate the length on disk of an fd entry's info uint32_t scap_fd_info_len(scap_fdinfo* fdi); // Write the given fd info to disk int32_t scap_fd_write_to_disk(scap_t* handle, scap_fdinfo* fdi, scap_dumper_t* dumper, uint32_t len); // Populate the given fd by reading the info from disk uint32_t scap_fd_read_from_disk(scap_t* handle, OUT scap_fdinfo* fdi, OUT size_t* nbytes, uint32_t block_type, gzFile f); // Parse the headers of a trace file and load the tables int32_t scap_read_init(scap_t* handle, gzFile f); // Add the file descriptor info pointed by fdi to the fd table for process pi. // Note: silently skips if fdi->type is SCAP_FD_UNKNOWN. int32_t scap_add_fd_to_proc_table(scap_t* handle, scap_threadinfo* pi, scap_fdinfo* fdi, char *error); // Remove the given fd from the process table of the process pointed by pi void scap_fd_remove(scap_t* handle, scap_threadinfo* pi, int64_t fd); // Read an event from disk int32_t scap_next_offline(scap_t* handle, OUT scap_evt** pevent, OUT uint16_t* pcpuid); // read the file descriptors for a given process directory int32_t scap_fd_scan_fd_dir(scap_t* handle, char * procdir, scap_threadinfo* pi, struct scap_ns_socket_list** sockets_by_ns, char *error); // read tcp or udp sockets from the proc filesystem int32_t scap_fd_read_ipv4_sockets_from_proc_fs(scap_t* handle, const char * dir, int l4proto, scap_fdinfo ** sockets); // read all sockets and add them to the socket table hashed by their ino int32_t scap_fd_read_sockets(scap_t* handle, char* procdir, struct scap_ns_socket_list* sockets, char *error); // prints procs details for a give tid void scap_proc_print_proc_by_tid(scap_t* handle, uint64_t tid); // Allocate and return the list of interfaces on this system int32_t scap_create_iflist(scap_t* handle); // Free a previously allocated list of interfaces void scap_free_iflist(scap_addrlist* ifhandle); // Allocate and return the list of users on this system int32_t scap_create_userlist(scap_t* handle); // Free a previously allocated list of users void scap_free_userlist(scap_userlist* uhandle); int32_t scap_fd_post_process_unix_sockets(scap_t* handle, scap_fdinfo* sockets); int32_t scap_proc_fill_cgroups(scap_t *handle, struct scap_threadinfo* tinfo, const char* procdirname); bool scap_alloc_proclist_info(scap_t* handle, uint32_t n_entries); // Determine whether or not the provided event should be suppressed, // based on its event type and parameters. May update the set of // suppressed tids as a side-effect. // // Returns SCAP_FAILURE if we tried to add the tid to the suppressed // tid set, but it could *not* be added, SCAP_SUCCESS otherwise. int32_t scap_check_suppressed(scap_t *handle, scap_evt *pevent, bool *suppressed); // Possibly add or remove the provided comm, tid combination to the // set of suppressed processes. If the ptid is currently in the // suppressed set, the tid will always be added to the suppressed // set. Otherwise, the tid will be added if the comm matches an entry // in suppressed_comms. // // Sets *suppressed to whether, after this check, the tid is suppressed. // // Returns SCAP_FAILURE if we tried to add the tid to the suppressed // tid set, but it could *not* be added, SCAP_SUCCESS otherwise. int32_t scap_update_suppressed(scap_t *handle, const char *comm, uint64_t tid, uint64_t ptid, bool *suppressed); // Wrapper around strerror using buffer in handle const char *scap_strerror(scap_t *handle, int errnum); // // ASSERT implementation // #ifdef ASSERT #undef ASSERT #endif // ASSERT #ifdef _DEBUG #define ASSERT(X) assert(X) #else // _DEBUG #define ASSERT(X) #endif // _DEBUG #define CHECK_READ_SIZE(read_size, expected_size) if(read_size != expected_size) \ {\ snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "expecting %d bytes, read %d at %s, line %d. Is the file truncated?",\ (int)expected_size,\ (int)read_size,\ __FILE__,\ __LINE__);\ return SCAP_FAILURE;\ } #define CHECK_READ_SIZE_WITH_FREE(alloc_buffer, read_size, expected_size) if(read_size != expected_size) \ {\ snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "expecting %d bytes, read %d at %s, line %d. Is the file truncated?",\ (int)expected_size,\ (int)read_size,\ __FILE__,\ __LINE__);\ free(alloc_buffer);\ return SCAP_FAILURE;\ } // // Useful stuff // #ifndef MIN #define MIN(X,Y) ((X) < (Y)? (X):(Y)) #define MAX(X,Y) ((X) > (Y)? (X):(Y)) #endif // // Driver proc info table sizes // #define SCAP_DRIVER_PROCINFO_INITIAL_SIZE 7 #define SCAP_DRIVER_PROCINFO_MAX_SIZE 128000 extern const enum ppm_syscall_code g_syscall_code_routing_table[]; extern const struct syscall_evt_pair g_syscall_table[]; extern const struct ppm_event_info g_event_info[]; extern const struct ppm_syscall_desc g_syscall_info_table[]; extern const struct ppm_event_entry g_ppm_events[]; extern bool validate_info_table_size(); #ifdef __cplusplus } #endif sysdig-0.26.4/userspace/libscap/scap.c000066400000000000000000001371621352731327100176240ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #ifndef _WIN32 #include #include #include #include #include #include #include #include #include #endif // _WIN32 #ifdef CYGWING_AGENT #define DRAGENT_WIN_HAL_C_ONLY #include #endif #include "scap.h" #ifdef HAS_CAPTURE #ifndef CYGWING_AGENT #include "driver_config.h" #endif // CYGWING_AGENT #endif // HAS_CAPTURE #include "../../driver/ppm_ringbuffer.h" #include "scap_savefile.h" #include "scap-int.h" #if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT) #include "scap_bpf.h" #endif //#define NDEBUG #include static const char *SYSDIG_BPF_PROBE_ENV = "SYSDIG_BPF_PROBE"; // // Probe version string size // #define SCAP_PROBE_VERSION_SIZE 32 const char* scap_getlasterr(scap_t* handle) { return handle ? handle->m_lasterr : "null scap handle"; } static int32_t copy_comms(scap_t *handle, const char **suppressed_comms) { if(suppressed_comms) { uint32_t i; const char *comm; for(i = 0, comm = suppressed_comms[i]; comm && i < SCAP_MAX_SUPPRESSED_COMMS; i++, comm = suppressed_comms[i]) { int32_t res; if((res = scap_suppress_events_comm(handle, comm)) != SCAP_SUCCESS) { return res; } } } return SCAP_SUCCESS; } #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) scap_t* scap_open_live_int(char *error, int32_t *rc, proc_entry_callback proc_callback, void* proc_callback_context, bool import_users, const char *bpf_probe, const char **suppressed_comms) { snprintf(error, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); *rc = SCAP_NOT_SUPPORTED; return NULL; } #else static uint32_t get_max_consumers() { uint32_t max; FILE *pfile = fopen("/sys/module/" PROBE_DEVICE_NAME "_probe/parameters/max_consumers", "r"); if(pfile != NULL) { int w = fscanf(pfile, "%"PRIu32, &max); if(w == 0) { return 0; } fclose(pfile); return max; } return 0; } scap_t* scap_open_live_int(char *error, int32_t *rc, proc_entry_callback proc_callback, void* proc_callback_context, bool import_users, const char *bpf_probe, const char **suppressed_comms) { uint32_t j; char filename[SCAP_MAX_PATH_SIZE]; scap_t* handle = NULL; uint32_t ndevs; // // Allocate the handle // handle = (scap_t*) calloc(sizeof(scap_t), 1); if(!handle) { snprintf(error, SCAP_LASTERR_SIZE, "error allocating the scap_t structure"); *rc = SCAP_FAILURE; return NULL; } // // Preliminary initializations // handle->m_mode = SCAP_MODE_LIVE; // // While in theory we could always rely on the scap caller to properly // set a BPF probe from the environment variable, it's in practice easier // to do one more check here in scap so we don't have to repeat the logic // in all the possible users of the libraries (falco, sysdig, csysdig, dragent, ...) // if(!bpf_probe) { bpf_probe = scap_get_bpf_probe_from_env(); } char buf[SCAP_MAX_PATH_SIZE]; if(bpf_probe) { handle->m_bpf = true; if(strlen(bpf_probe) == 0) { const char *home = getenv("HOME"); if(!home) { scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "HOME environment not set"); *rc = SCAP_FAILURE; return NULL; } snprintf(buf, sizeof(buf), "%s/.sysdig/%s-bpf.o", home, PROBE_NAME); bpf_probe = buf; } } else { handle->m_bpf = false; } handle->m_ncpus = sysconf(_SC_NPROCESSORS_CONF); if(handle->m_ncpus == -1) { scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "_SC_NPROCESSORS_CONF: %s", scap_strerror(handle, errno)); *rc = SCAP_FAILURE; return NULL; } // // Find out how many devices we have to open, which equals to the number of CPUs // ndevs = sysconf(_SC_NPROCESSORS_ONLN); if(ndevs == -1) { scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "_SC_NPROCESSORS_ONLN: %s", scap_strerror(handle, errno)); *rc = SCAP_FAILURE; return NULL; } handle->m_devs = (scap_device*) calloc(sizeof(scap_device), ndevs); if(!handle->m_devs) { scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "error allocating the device handles"); *rc = SCAP_FAILURE; return NULL; } for(j = 0; j < ndevs; j++) { handle->m_devs[j].m_buffer = (char*)MAP_FAILED; if(!handle->m_bpf) { handle->m_devs[j].m_bufinfo = (struct ppm_ring_buffer_info*)MAP_FAILED; } } handle->m_ndevs = ndevs; // // Extract machine information // handle->m_proc_callback = proc_callback; handle->m_proc_callback_context = proc_callback_context; handle->m_machine_info.num_cpus = sysconf(_SC_NPROCESSORS_ONLN); handle->m_machine_info.memory_size_bytes = (uint64_t)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE); gethostname(handle->m_machine_info.hostname, sizeof(handle->m_machine_info.hostname) / sizeof(handle->m_machine_info.hostname[0])); handle->m_machine_info.reserved1 = 0; handle->m_machine_info.reserved2 = 0; handle->m_machine_info.reserved3 = 0; handle->m_machine_info.reserved4 = 0; handle->m_driver_procinfo = NULL; handle->m_fd_lookup_limit = 0; #ifdef CYGWING_AGENT handle->m_whh = NULL; #endif // // Create the interface list // if((*rc = scap_create_iflist(handle)) != SCAP_SUCCESS) { scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "error creating the interface list"); return NULL; } // // Create the user list // if(import_users) { if((*rc = scap_create_userlist(handle)) != SCAP_SUCCESS) { scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "error creating the interface list"); return NULL; } } else { handle->m_userlist = NULL; } handle->m_fake_kernel_proc.tid = -1; handle->m_fake_kernel_proc.pid = -1; handle->m_fake_kernel_proc.flags = 0; snprintf(handle->m_fake_kernel_proc.comm, SCAP_MAX_PATH_SIZE, "kernel"); snprintf(handle->m_fake_kernel_proc.exe, SCAP_MAX_PATH_SIZE, "kernel"); handle->m_fake_kernel_proc.args[0] = 0; handle->refresh_proc_table_when_saving = true; handle->m_suppressed_comms = NULL; handle->m_num_suppressed_comms = 0; handle->m_suppressed_tids = NULL; handle->m_num_suppressed_evts = 0; handle->m_buffer_empty_wait_time_us = BUFFER_EMPTY_WAIT_TIME_US_START; if ((*rc = copy_comms(handle, suppressed_comms)) != SCAP_SUCCESS) { scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "error copying suppressed comms"); return NULL; } // // Open and initialize all the devices // if(handle->m_bpf) { if((*rc = scap_bpf_load(handle, bpf_probe)) != SCAP_SUCCESS) { snprintf(error, SCAP_LASTERR_SIZE, "%s", handle->m_lasterr); scap_close(handle); return NULL; } } else { int len; uint32_t all_scanned_devs; // // Allocate the device descriptors. // len = RING_BUF_SIZE * 2; for(j = 0, all_scanned_devs = 0; j < handle->m_ndevs && all_scanned_devs < handle->m_ncpus; ++all_scanned_devs) { // // Open the device // snprintf(filename, sizeof(filename), "%s/dev/" PROBE_DEVICE_NAME "%d", scap_get_host_root(), all_scanned_devs); if((handle->m_devs[j].m_fd = open(filename, O_RDWR | O_SYNC)) < 0) { if(errno == ENODEV) { // // This CPU is offline, so we just skip it // continue; } else if(errno == EBUSY) { uint32_t curr_max_consumers = get_max_consumers(); snprintf(error, SCAP_LASTERR_SIZE, "Too many sysdig instances attached to device %s. Current value for /sys/module/" PROBE_DEVICE_NAME "_probe/parameters/max_consumers is '%"PRIu32"'.", filename, curr_max_consumers); } else { snprintf(error, SCAP_LASTERR_SIZE, "error opening device %s. Make sure you have root credentials and that the " PROBE_NAME " module is loaded.", filename); } scap_close(handle); *rc = SCAP_FAILURE; return NULL; } // Set close-on-exec for the fd if (fcntl(handle->m_devs[j].m_fd, F_SETFD, FD_CLOEXEC) == -1) { snprintf(error, SCAP_LASTERR_SIZE, "Can not set close-on-exec flag for fd for device %s (%s)", filename, scap_strerror(handle, errno)); scap_close(handle); *rc = SCAP_FAILURE; return NULL; } // // Map the ring buffer // handle->m_devs[j].m_buffer = (char*)mmap(0, len, PROT_READ, MAP_SHARED, handle->m_devs[j].m_fd, 0); if(handle->m_devs[j].m_buffer == MAP_FAILED) { // we cleanup this fd and then we let scap_close() take care of the other ones close(handle->m_devs[j].m_fd); scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "error mapping the ring buffer for device %s", filename); *rc = SCAP_FAILURE; return NULL; } // // Map the ppm_ring_buffer_info that contains the buffer pointers // handle->m_devs[j].m_bufinfo = (struct ppm_ring_buffer_info*)mmap(0, sizeof(struct ppm_ring_buffer_info), PROT_READ | PROT_WRITE, MAP_SHARED, handle->m_devs[j].m_fd, 0); if(handle->m_devs[j].m_bufinfo == MAP_FAILED) { // we cleanup this fd and then we let scap_close() take care of the other ones munmap(handle->m_devs[j].m_buffer, len); close(handle->m_devs[j].m_fd); scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "error mapping the ring buffer info for device %s", filename); *rc = SCAP_FAILURE; return NULL; } ++j; } } for(j = 0; j < handle->m_ndevs; ++j) { // // Additional initializations // handle->m_devs[j].m_lastreadsize = 0; handle->m_devs[j].m_sn_len = 0; scap_stop_dropping_mode(handle); } // // Create the process list // error[0] = '\0'; snprintf(filename, sizeof(filename), "%s/proc", scap_get_host_root()); if((*rc = scap_proc_scan_proc_dir(handle, filename, error)) != SCAP_SUCCESS) { scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "error creating the process list. Make sure you have root credentials."); return NULL; } // // Now that sysdig has done all its /proc parsing, start the capture // if((*rc = scap_start_capture(handle)) != SCAP_SUCCESS) { scap_close(handle); return NULL; } return handle; } #endif // !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) scap_t* scap_open_offline_int(gzFile gzfile, char *error, int32_t *rc, proc_entry_callback proc_callback, void* proc_callback_context, bool import_users, uint64_t start_offset, const char **suppressed_comms) { scap_t* handle = NULL; // // Allocate the handle // handle = (scap_t*)malloc(sizeof(scap_t)); if(!handle) { snprintf(error, SCAP_LASTERR_SIZE, "error allocating the scap_t structure"); *rc = SCAP_FAILURE; return NULL; } // // Preliminary initializations // handle->m_mode = SCAP_MODE_CAPTURE; handle->m_proc_callback = proc_callback; handle->m_proc_callback_context = proc_callback_context; handle->m_devs = NULL; handle->m_ndevs = 0; handle->m_proclist = NULL; handle->m_dev_list = NULL; handle->m_evtcnt = 0; handle->m_file = NULL; handle->m_addrlist = NULL; handle->m_userlist = NULL; handle->m_machine_info.num_cpus = (uint32_t)-1; handle->m_last_evt_dump_flags = 0; handle->m_driver_procinfo = NULL; handle->refresh_proc_table_when_saving = true; handle->m_fd_lookup_limit = 0; #ifdef CYGWING_AGENT handle->m_whh = NULL; #endif handle->m_bpf = false; handle->m_suppressed_comms = NULL; handle->m_suppressed_tids = NULL; handle->m_file_evt_buf = (char*)malloc(FILE_READ_BUF_SIZE); if(!handle->m_file_evt_buf) { snprintf(error, SCAP_LASTERR_SIZE, "error allocating the read buffer"); scap_close(handle); *rc = SCAP_FAILURE; return NULL; } handle->m_file = gzfile; // // If this is a merged file, we might have to move the read offset to the next section // if(start_offset != 0) { scap_fseek(handle, start_offset); } // // Validate the file and load the non-event blocks // if((*rc = scap_read_init(handle, handle->m_file)) != SCAP_SUCCESS) { snprintf(error, SCAP_LASTERR_SIZE, "Could not initialize reader: %s", scap_getlasterr(handle)); scap_close(handle); return NULL; } if(!import_users) { if(handle->m_userlist != NULL) { scap_free_userlist(handle->m_userlist); handle->m_userlist = NULL; } } // // Add the fake process for kernel threads // handle->m_fake_kernel_proc.tid = -1; handle->m_fake_kernel_proc.pid = -1; handle->m_fake_kernel_proc.flags = 0; snprintf(handle->m_fake_kernel_proc.comm, SCAP_MAX_PATH_SIZE, "kernel"); snprintf(handle->m_fake_kernel_proc.exe, SCAP_MAX_PATH_SIZE, "kernel"); handle->m_fake_kernel_proc.args[0] = 0; handle->m_num_suppressed_comms = 0; handle->m_num_suppressed_evts = 0; if ((*rc = copy_comms(handle, suppressed_comms)) != SCAP_SUCCESS) { scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "error copying suppressed comms"); return NULL; } return handle; } scap_t* scap_open_offline(const char* fname, char *error, int32_t* rc) { gzFile gzfile = gzopen(fname, "rb"); if(gzfile == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "can't open file %s", fname); *rc = SCAP_FAILURE; return NULL; } return scap_open_offline_int(gzfile, error, rc, NULL, NULL, true, 0, NULL); } scap_t* scap_open_offline_fd(int fd, char *error, int32_t *rc) { gzFile gzfile = gzdopen(fd, "rb"); if(gzfile == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "can't open fd %d", fd); *rc = SCAP_FAILURE; return NULL; } return scap_open_offline_int(gzfile, error, rc, NULL, NULL, true, 0, NULL); } scap_t* scap_open_live(char *error, int32_t *rc) { return scap_open_live_int(error, rc, NULL, NULL, true, NULL, NULL); } scap_t* scap_open_nodriver_int(char *error, int32_t *rc, proc_entry_callback proc_callback, void* proc_callback_context, bool import_users) { #if !defined(HAS_CAPTURE) snprintf(error, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); *rc = SCAP_NOT_SUPPORTED; return NULL; #else char filename[SCAP_MAX_PATH_SIZE]; scap_t* handle = NULL; // // Allocate the handle // handle = (scap_t*)malloc(sizeof(scap_t)); if(!handle) { snprintf(error, SCAP_LASTERR_SIZE, "error allocating the scap_t structure"); *rc = SCAP_FAILURE; return NULL; } // // Preliminary initializations // memset(handle, 0, sizeof(scap_t)); handle->m_mode = SCAP_MODE_NODRIVER; // // Extract machine information // handle->m_proc_callback = proc_callback; handle->m_proc_callback_context = proc_callback_context; handle->m_machine_info.num_cpus = sysconf(_SC_NPROCESSORS_ONLN); handle->m_machine_info.memory_size_bytes = (uint64_t)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE); gethostname(handle->m_machine_info.hostname, sizeof(handle->m_machine_info.hostname) / sizeof(handle->m_machine_info.hostname[0])); handle->m_machine_info.reserved1 = 0; handle->m_machine_info.reserved2 = 0; handle->m_machine_info.reserved3 = 0; handle->m_machine_info.reserved4 = 0; handle->m_driver_procinfo = NULL; handle->m_fd_lookup_limit = SCAP_NODRIVER_MAX_FD_LOOKUP; // fd lookup is limited here because is very expensive // // If this is part of the windows agent, open the windows HAL // #ifdef CYGWING_AGENT handle->m_whh = wh_open(error); if(handle->m_whh == NULL) { scap_close(handle); *rc = SCAP_FAILURE; return NULL; } #endif // // Create the interface list // if((*rc = scap_create_iflist(handle)) != SCAP_SUCCESS) { scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "error creating the interface list"); return NULL; } // // Create the user list // if(import_users) { if((*rc = scap_create_userlist(handle)) != SCAP_SUCCESS) { scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "error creating the interface list"); return NULL; } } else { handle->m_userlist = NULL; } handle->m_fake_kernel_proc.tid = -1; handle->m_fake_kernel_proc.pid = -1; handle->m_fake_kernel_proc.flags = 0; snprintf(handle->m_fake_kernel_proc.comm, SCAP_MAX_PATH_SIZE, "kernel"); snprintf(handle->m_fake_kernel_proc.exe, SCAP_MAX_PATH_SIZE, "kernel"); handle->m_fake_kernel_proc.args[0] = 0; handle->refresh_proc_table_when_saving = true; // // Create the process list // error[0] = '\0'; snprintf(filename, sizeof(filename), "%s/proc", scap_get_host_root()); if((*rc = scap_proc_scan_proc_dir(handle, filename, error)) != SCAP_SUCCESS) { scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "error creating the process list. Make sure you have root credentials."); return NULL; } return handle; #endif // HAS_CAPTURE } scap_t* scap_open(scap_open_args args, char *error, int32_t *rc) { switch(args.mode) { case SCAP_MODE_CAPTURE: { gzFile gzfile; if(args.fd != 0) { gzfile = gzdopen(args.fd, "rb"); } else { gzfile = gzopen(args.fname, "rb"); } if(gzfile == NULL) { if(args.fd != 0) { snprintf(error, SCAP_LASTERR_SIZE, "can't open fd %d", args.fd); } else { snprintf(error, SCAP_LASTERR_SIZE, "can't open file %s", args.fname); } *rc = SCAP_FAILURE; return NULL; } return scap_open_offline_int(gzfile, error, rc, args.proc_callback, args.proc_callback_context, args.import_users, args.start_offset, args.suppressed_comms); } case SCAP_MODE_LIVE: #ifndef CYGWING_AGENT return scap_open_live_int(error, rc, args.proc_callback, args.proc_callback_context, args.import_users, args.bpf_probe, args.suppressed_comms); #else snprintf(error, SCAP_LASTERR_SIZE, "scap_open: live mode currently not supported on windows. Use nodriver mode instead."); *rc = SCAP_NOT_SUPPORTED; return NULL; #endif case SCAP_MODE_NODRIVER: return scap_open_nodriver_int(error, rc, args.proc_callback, args.proc_callback_context, args.import_users); case SCAP_MODE_NONE: // error break; } snprintf(error, SCAP_LASTERR_SIZE, "incorrect mode %d", args.mode); *rc = SCAP_FAILURE; return NULL; } void scap_close(scap_t* handle) { if(handle->m_file) { gzclose(handle->m_file); } else if(handle->m_mode == SCAP_MODE_LIVE) { #if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT) uint32_t j; ASSERT(handle->m_file == NULL); if(handle->m_devs != NULL) { if(handle->m_bpf) { if(scap_bpf_close(handle) != SCAP_SUCCESS) { ASSERT(false); } } else { // // Destroy all the device descriptors // for(j = 0; j < handle->m_ndevs; j++) { if(handle->m_devs[j].m_buffer != MAP_FAILED) { munmap(handle->m_devs[j].m_bufinfo, sizeof(struct ppm_ring_buffer_info)); munmap(handle->m_devs[j].m_buffer, RING_BUF_SIZE * 2); close(handle->m_devs[j].m_fd); } } } // // Free the memory // free(handle->m_devs); } #endif // HAS_CAPTURE } #ifdef CYGWING_AGENT if(handle->m_whh != NULL) { wh_close(handle->m_whh); } #endif if(handle->m_file_evt_buf) { free(handle->m_file_evt_buf); } // Free the process table if(handle->m_proclist != NULL) { scap_proc_free_table(handle); } // Free the device table if(handle->m_dev_list != NULL) { scap_free_device_table(handle); } // Free the interface list if(handle->m_addrlist) { scap_free_iflist(handle->m_addrlist); } // Free the user list if(handle->m_userlist) { scap_free_userlist(handle->m_userlist); } if(handle->m_driver_procinfo) { free(handle->m_driver_procinfo); handle->m_driver_procinfo = NULL; } if(handle->m_suppressed_comms) { uint32_t i; for(i=0; i < handle->m_num_suppressed_comms; i++) { free(handle->m_suppressed_comms[i]); } free(handle->m_suppressed_comms); handle->m_suppressed_comms = NULL; } if(handle->m_suppressed_tids) { struct scap_tid *tid; struct scap_tid *ttid; HASH_ITER(hh, handle->m_suppressed_tids, tid, ttid) { HASH_DEL(handle->m_suppressed_tids, tid); free(tid); } handle->m_suppressed_tids = NULL; } // // Release the handle // free(handle); } scap_os_platform scap_get_os_platform(scap_t* handle) { #if defined(_M_IX86) || defined(__i386__) #ifdef linux return SCAP_PFORM_LINUX_I386; #else return SCAP_PFORM_WINDOWS_I386; #endif // linux #else #if defined(_M_X64) || defined(__AMD64__) #ifdef linux return SCAP_PFORM_LINUX_X64; #else return SCAP_PFORM_WINDOWS_X64; #endif // linux #else return SCAP_PFORM_UNKNOWN; #endif // defined(_M_X64) || defined(__AMD64__) #endif // defined(_M_IX86) || defined(__i386__) } uint32_t scap_get_ndevs(scap_t* handle) { return handle->m_ndevs; } #if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT) #ifndef _WIN32 static inline void get_buf_pointers(struct ppm_ring_buffer_info* bufinfo, uint32_t* phead, uint32_t* ptail, uint64_t* pread_size) #else void get_buf_pointers(struct ppm_ring_buffer_info* bufinfo, uint32_t* phead, uint32_t* ptail, uint64_t* pread_size) #endif { *phead = bufinfo->head; *ptail = bufinfo->tail; if(*ptail > *phead) { *pread_size = RING_BUF_SIZE - *ptail + *phead; } else { *pread_size = *phead - *ptail; } } static void scap_advance_tail(scap_t* handle, uint32_t cpuid) { uint32_t ttail; if(handle->m_bpf) { return scap_bpf_advance_tail(handle, cpuid); } // // Update the tail based on the amount of data read in the *previous* call. // Tail is never updated when we serve the data, because we assume that the caller is using // the buffer we give to her until she calls us again. // ttail = handle->m_devs[cpuid].m_bufinfo->tail + handle->m_devs[cpuid].m_lastreadsize; // // Make sure every read of the old buffer is completed before we move the tail and the // producer (on another CPU) can start overwriting it. // I use this instead of asm(mfence) because it should be portable even on the weirdest // CPUs // __sync_synchronize(); if(ttail < RING_BUF_SIZE) { handle->m_devs[cpuid].m_bufinfo->tail = ttail; } else { handle->m_devs[cpuid].m_bufinfo->tail = ttail - RING_BUF_SIZE; } handle->m_devs[cpuid].m_lastreadsize = 0; } int32_t scap_readbuf(scap_t* handle, uint32_t cpuid, OUT char** buf, OUT uint32_t* len) { uint32_t thead; uint32_t ttail; uint64_t read_size; if(handle->m_bpf) { return scap_bpf_readbuf(handle, cpuid, buf, len); } // // Read the pointers. // get_buf_pointers(handle->m_devs[cpuid].m_bufinfo, &thead, &ttail, &read_size); // // Remember read_size so we can update the tail at the next call // handle->m_devs[cpuid].m_lastreadsize = read_size; // // Return the results // *len = read_size; *buf = handle->m_devs[cpuid].m_buffer + ttail; return SCAP_SUCCESS; } static bool are_buffers_empty(scap_t* handle) { uint32_t j; for(j = 0; j < handle->m_ndevs; j++) { uint64_t read_size; if(handle->m_bpf) { uint64_t thead; uint64_t ttail; scap_bpf_get_buf_pointers(handle->m_devs[j].m_buffer, &thead, &ttail, &read_size); } else { uint32_t thead; uint32_t ttail; get_buf_pointers(handle->m_devs[j].m_bufinfo, &thead, &ttail, &read_size); } if(read_size > BUFFER_EMPTY_THRESHOLD_B) { return false; } } return true; } int32_t refill_read_buffers(scap_t* handle) { uint32_t j; uint32_t ndevs = handle->m_ndevs; if(are_buffers_empty(handle)) { usleep(handle->m_buffer_empty_wait_time_us); handle->m_buffer_empty_wait_time_us = MIN(handle->m_buffer_empty_wait_time_us * 2, BUFFER_EMPTY_WAIT_TIME_US_MAX); } else { handle->m_buffer_empty_wait_time_us = BUFFER_EMPTY_WAIT_TIME_US_START; } // // Refill our data for each of the devices // for(j = 0; j < ndevs; j++) { struct scap_device *dev = &(handle->m_devs[j]); int32_t res = scap_readbuf(handle, j, &dev->m_sn_next_event, &dev->m_sn_len); if(res != SCAP_SUCCESS) { return res; } } // // Note: we might return a spurious timeout here in case the previous loop extracted valid data to parse. // It's ok, since this is rare and the caller will just call us again after receiving a // SCAP_TIMEOUT. // return SCAP_TIMEOUT; } #endif // HAS_CAPTURE #ifndef _WIN32 static inline int32_t scap_next_live(scap_t* handle, OUT scap_evt** pevent, OUT uint16_t* pcpuid) #else static int32_t scap_next_live(scap_t* handle, OUT scap_evt** pevent, OUT uint16_t* pcpuid) #endif { #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) // // this should be prevented at open time // ASSERT(false); return SCAP_FAILURE; #else uint32_t j; uint64_t max_ts = 0xffffffffffffffffLL; scap_evt* pe = NULL; uint32_t ndevs = handle->m_ndevs; *pcpuid = 65535; for(j = 0; j < ndevs; j++) { scap_device* dev = &(handle->m_devs[j]); if(dev->m_sn_len == 0) { // // If we don't have data from this ring, but we are // still occupying, free the resources for the // producer rather than sitting on them. // if(dev->m_lastreadsize > 0) { scap_advance_tail(handle, j); } continue; } if(handle->m_bpf) { pe = scap_bpf_evt_from_perf_sample(dev->m_sn_next_event); } else { pe = (scap_evt *) dev->m_sn_next_event; } // // We want to consume the event with the lowest timestamp // if(pe->ts < max_ts) { if(pe->len > dev->m_sn_len) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_next buffer corruption"); // // if you get the following assertion, first recompile the driver and libscap // ASSERT(false); return SCAP_FAILURE; } *pevent = pe; *pcpuid = j; max_ts = pe->ts; } } // // Check which buffer has been picked // if(*pcpuid != 65535) { struct scap_device *dev = &handle->m_devs[*pcpuid]; // // Update the pointers. // if(handle->m_bpf) { scap_bpf_advance_to_evt(handle, *pcpuid, true, dev->m_sn_next_event, &dev->m_sn_next_event, &dev->m_sn_len); } else { ASSERT(dev->m_sn_len >= (*pevent)->len); dev->m_sn_len -= (*pevent)->len; dev->m_sn_next_event += (*pevent)->len; } return SCAP_SUCCESS; } else { // // All the buffers have been consumed. Check if there's enough data to keep going or // if we should wait. // return refill_read_buffers(handle); } #endif } #ifndef _WIN32 static int32_t scap_next_nodriver(scap_t* handle, OUT scap_evt** pevent, OUT uint16_t* pcpuid) { static scap_evt evt; evt.len = 0; evt.tid = -1; evt.type = PPME_SYSDIGEVENT_X; evt.nparams = 0; usleep(100000); struct timeval tv; gettimeofday(&tv, NULL); evt.ts = tv.tv_sec * (uint64_t) 1000000000 + tv.tv_usec * 1000; *pevent = &evt; return SCAP_SUCCESS; } #endif // _WIN32 int32_t scap_next(scap_t* handle, OUT scap_evt** pevent, OUT uint16_t* pcpuid) { int32_t res = SCAP_FAILURE; switch(handle->m_mode) { case SCAP_MODE_CAPTURE: res = scap_next_offline(handle, pevent, pcpuid); break; case SCAP_MODE_LIVE: res = scap_next_live(handle, pevent, pcpuid); break; #ifndef _WIN32 case SCAP_MODE_NODRIVER: res = scap_next_nodriver(handle, pevent, pcpuid); break; #endif case SCAP_MODE_NONE: res = SCAP_FAILURE; } if(res == SCAP_SUCCESS) { bool suppressed; // Check to see if the event should be suppressed due // to coming from a supressed tid if((res = scap_check_suppressed(handle, *pevent, &suppressed)) != SCAP_SUCCESS) { return res; } if(suppressed) { handle->m_num_suppressed_evts++; return SCAP_TIMEOUT; } else { handle->m_evtcnt++; } } return res; } // // Return the process list for the given handle // scap_threadinfo* scap_get_proc_table(scap_t* handle) { return handle->m_proclist; } // // Return the number of dropped events for the given handle // int32_t scap_get_stats(scap_t* handle, OUT scap_stats* stats) { uint32_t j; stats->n_evts = 0; stats->n_drops = 0; stats->n_drops_buffer = 0; stats->n_drops_pf = 0; stats->n_drops_bug = 0; stats->n_preemptions = 0; stats->n_suppressed = handle->m_num_suppressed_evts; stats->n_tids_suppressed = HASH_COUNT(handle->m_suppressed_tids); #if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT) if(handle->m_bpf) { return scap_bpf_get_stats(handle, stats); } else { for(j = 0; j < handle->m_ndevs; j++) { stats->n_evts += handle->m_devs[j].m_bufinfo->n_evts; stats->n_drops_buffer += handle->m_devs[j].m_bufinfo->n_drops_buffer; stats->n_drops_pf += handle->m_devs[j].m_bufinfo->n_drops_pf; stats->n_drops += handle->m_devs[j].m_bufinfo->n_drops_buffer + handle->m_devs[j].m_bufinfo->n_drops_pf; stats->n_preemptions += handle->m_devs[j].m_bufinfo->n_preemptions; } } #endif return SCAP_SUCCESS; } // // Stop capturing the events // int32_t scap_stop_capture(scap_t* handle) { #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else uint32_t j; // // Not supported for files // if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "cannot stop not live captures"); ASSERT(false); return SCAP_FAILURE; } // // Disable capture on all the rings // for(j = 0; j < handle->m_ndevs; j++) { if(handle->m_bpf) { return scap_bpf_stop_capture(handle); } else { if(ioctl(handle->m_devs[j].m_fd, PPM_IOCTL_DISABLE_CAPTURE)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_stop_capture failed for device %" PRIu32, j); ASSERT(false); return SCAP_FAILURE; } } } return SCAP_SUCCESS; #endif // HAS_CAPTURE } // // Start capturing the events // int32_t scap_start_capture(scap_t* handle) { #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else uint32_t j; // // Not supported for files // if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "cannot start capture on this scap mode"); ASSERT(false); return SCAP_FAILURE; } // // Enable capture on all the rings // if(handle->m_bpf) { return scap_bpf_start_capture(handle); } else { for(j = 0; j < handle->m_ndevs; j++) { if(ioctl(handle->m_devs[j].m_fd, PPM_IOCTL_ENABLE_CAPTURE)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_start_capture failed for device %" PRIu32, j); ASSERT(false); return SCAP_FAILURE; } } } return SCAP_SUCCESS; #endif // HAS_CAPTURE } #if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT) static int32_t scap_set_dropping_mode(scap_t* handle, int request, uint32_t sampling_ratio) { // // Not supported for files // if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "%s: dropping not supported in replay mode", __FUNCTION__); ASSERT(false); return SCAP_FAILURE; } if(handle->m_ndevs) { ASSERT((request == PPM_IOCTL_ENABLE_DROPPING_MODE && ((sampling_ratio == 1) || (sampling_ratio == 2) || (sampling_ratio == 4) || (sampling_ratio == 8) || (sampling_ratio == 16) || (sampling_ratio == 32) || (sampling_ratio == 64) || (sampling_ratio == 128))) || (request == PPM_IOCTL_DISABLE_DROPPING_MODE)); if(ioctl(handle->m_devs[0].m_fd, request, sampling_ratio)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "%s, request %d for sampling ratio %u: %s", __FUNCTION__, request, sampling_ratio, scap_strerror(handle, errno)); ASSERT(false); return SCAP_FAILURE; } } return SCAP_SUCCESS; } #endif #if defined(HAS_CAPTURE) && ! defined(CYGWING_AGENT) int32_t scap_enable_tracers_capture(scap_t* handle) { // // Not supported for files // if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_enable_tracers_capture not supported on this scap mode"); ASSERT(false); return SCAP_FAILURE; } if(handle->m_ndevs) { if(handle->m_bpf) { return scap_bpf_enable_tracers_capture(handle); } else { if(ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_SET_TRACERS_CAPTURE)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "%s failed", __FUNCTION__); ASSERT(false); return SCAP_FAILURE; } } } return SCAP_SUCCESS; } #endif #if defined(HAS_CAPTURE) && ! defined(CYGWING_AGENT) int32_t scap_enable_page_faults(scap_t *handle) { if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_enable_page_faults not supported on this scap mode"); ASSERT(false); return SCAP_FAILURE; } if(handle->m_ndevs) { if(handle->m_bpf) { return scap_bpf_enable_page_faults(handle); } else { if(ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_ENABLE_PAGE_FAULTS)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "%s failed", __FUNCTION__); ASSERT(false); return SCAP_FAILURE; } } } return SCAP_SUCCESS; } #endif int32_t scap_stop_dropping_mode(scap_t* handle) { #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else if(handle->m_bpf) { return scap_bpf_stop_dropping_mode(handle); } else { return scap_set_dropping_mode(handle, PPM_IOCTL_DISABLE_DROPPING_MODE, 0); } #endif } int32_t scap_start_dropping_mode(scap_t* handle, uint32_t sampling_ratio) { #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else if(handle->m_bpf) { return scap_bpf_start_dropping_mode(handle, sampling_ratio); } else { return scap_set_dropping_mode(handle, PPM_IOCTL_ENABLE_DROPPING_MODE, sampling_ratio); } #endif } // // Return the list of device addresses // scap_addrlist* scap_get_ifaddr_list(scap_t* handle) { return handle->m_addrlist; } // // Return the list of machine users // scap_userlist* scap_get_user_list(scap_t* handle) { return handle->m_userlist; } // // Get the machine information // const scap_machine_info* scap_get_machine_info(scap_t* handle) { if(handle->m_machine_info.num_cpus != (uint32_t)-1) { return (const scap_machine_info*)&handle->m_machine_info; } else { // // Reading from a file with no process info block // return NULL; } } int32_t scap_set_snaplen(scap_t* handle, uint32_t snaplen) { // // Not supported on files // if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "setting snaplen not supported on this scap mode"); return SCAP_FAILURE; } #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else if(handle->m_bpf) { return scap_bpf_set_snaplen(handle, snaplen); } else { // // Tell the driver to change the snaplen // if(ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_SET_SNAPLEN, snaplen)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_set_snaplen failed"); ASSERT(false); return SCAP_FAILURE; } { uint32_t j; // // Force a flush of the read buffers, so we don't capture events with the old snaplen // for(j = 0; j < handle->m_ndevs; j++) { scap_readbuf(handle, j, &handle->m_devs[j].m_sn_next_event, &handle->m_devs[j].m_sn_len); handle->m_devs[j].m_sn_len = 0; } } } return SCAP_SUCCESS; #endif } int64_t scap_get_readfile_offset(scap_t* handle) { if(handle->m_mode != SCAP_MODE_CAPTURE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_get_readfile_offset only works on captures"); return -1; } return gzoffset(handle->m_file); } #ifndef CYGWING_AGENT static int32_t scap_handle_eventmask(scap_t* handle, uint32_t op, uint32_t event_id) { if (handle == NULL) { return SCAP_FAILURE; } // // Not supported on files // if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "manipulating eventmasks not supported on this scap mode"); return SCAP_FAILURE; } #if !defined(HAS_CAPTURE) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "eventmask not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else // // Tell the driver to change the snaplen // switch(op) { case PPM_IOCTL_MASK_ZERO_EVENTS: case PPM_IOCTL_MASK_SET_EVENT: case PPM_IOCTL_MASK_UNSET_EVENT: break; default: snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "%s(%d) internal error", __FUNCTION__, op); ASSERT(false); return SCAP_FAILURE; break; } if(handle->m_bpf) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "eventmask not supported on bpf"); ASSERT(false); return SCAP_FAILURE; } else { if(ioctl(handle->m_devs[0].m_fd, op, event_id)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "%s(%d) failed for event type %d", __FUNCTION__, op, event_id); ASSERT(false); return SCAP_FAILURE; } { uint32_t j; // // Force a flush of the read buffers, so we don't capture events with the old snaplen // for(j = 0; j < handle->m_ndevs; j++) { scap_readbuf(handle, j, &handle->m_devs[j].m_sn_next_event, &handle->m_devs[j].m_sn_len); handle->m_devs[j].m_sn_len = 0; } } } return SCAP_SUCCESS; #endif // HAS_CAPTURE } #endif // CYGWING_AGENT int32_t scap_clear_eventmask(scap_t* handle) { #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "eventmask not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else return(scap_handle_eventmask(handle, PPM_IOCTL_MASK_ZERO_EVENTS, 0)); #endif } int32_t scap_set_eventmask(scap_t* handle, uint32_t event_id) { #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "eventmask not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else return(scap_handle_eventmask(handle, PPM_IOCTL_MASK_SET_EVENT, event_id)); #endif } int32_t scap_unset_eventmask(scap_t* handle, uint32_t event_id) { #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "eventmask not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else return(scap_handle_eventmask(handle, PPM_IOCTL_MASK_UNSET_EVENT, event_id)); #endif } uint32_t scap_event_get_dump_flags(scap_t* handle) { return handle->m_last_evt_dump_flags; } int32_t scap_enable_dynamic_snaplen(scap_t* handle) { // // Not supported on files // if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "setting snaplen not supported on this scap mode"); return SCAP_FAILURE; } #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else // // Tell the driver to change the snaplen // if(handle->m_bpf) { return scap_bpf_enable_dynamic_snaplen(handle); } else { if(ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_ENABLE_DYNAMIC_SNAPLEN)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_enable_dynamic_snaplen failed"); ASSERT(false); return SCAP_FAILURE; } } return SCAP_SUCCESS; #endif } int32_t scap_disable_dynamic_snaplen(scap_t* handle) { // // Not supported on files // if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "setting snaplen not supported on this scap mode"); return SCAP_FAILURE; } #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else // // Tell the driver to change the snaplen // if(handle->m_bpf) { return scap_bpf_disable_dynamic_snaplen(handle); } else { if(ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_DISABLE_DYNAMIC_SNAPLEN)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_disable_dynamic_snaplen failed"); ASSERT(false); return SCAP_FAILURE; } } return SCAP_SUCCESS; #endif } const char* scap_get_host_root() { char* p = getenv("SYSDIG_HOST_ROOT"); static char env_str[SCAP_MAX_PATH_SIZE + 1]; static bool inited = false; if (! inited) { strncpy(env_str, p ? p : "", SCAP_MAX_PATH_SIZE); env_str[SCAP_MAX_PATH_SIZE] = '\0'; inited = true; } return env_str; } bool scap_alloc_proclist_info(scap_t* handle, uint32_t n_entries) { uint32_t memsize; if(n_entries >= SCAP_DRIVER_PROCINFO_MAX_SIZE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "driver process list too big"); return false; } memsize = sizeof(struct ppm_proclist_info) + sizeof(struct ppm_proc_info) * n_entries; struct ppm_proclist_info *procinfo = (struct ppm_proclist_info*) realloc(handle->m_driver_procinfo, memsize); if(procinfo == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "driver process list allocation error"); return false; } if(handle->m_driver_procinfo == NULL) { procinfo->n_entries = 0; } procinfo->max_entries = n_entries; handle->m_driver_procinfo = procinfo; return true; } struct ppm_proclist_info* scap_get_threadlist(scap_t* handle) { // // Not supported on files // if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_get_threadlist not supported on this scap mode"); return NULL; } #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return NULL; #else if(handle->m_driver_procinfo == NULL) { if(scap_alloc_proclist_info(handle, SCAP_DRIVER_PROCINFO_INITIAL_SIZE) == false) { return NULL; } } if(handle->m_bpf) { return scap_bpf_get_threadlist(handle); } else { int ioctlres = ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_GET_PROCLIST, handle->m_driver_procinfo); if(ioctlres) { if(errno == ENOSPC) { if(scap_alloc_proclist_info(handle, handle->m_driver_procinfo->n_entries + 256) == false) { return NULL; } else { return scap_get_threadlist(handle); } } else { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Error calling PPM_IOCTL_GET_PROCLIST"); return NULL; } } } return handle->m_driver_procinfo; #endif // HAS_CAPTURE } void scap_set_refresh_proc_table_when_saving(scap_t* handle, bool refresh) { handle->refresh_proc_table_when_saving = refresh; } uint64_t scap_get_unexpected_block_readsize(scap_t* handle) { return handle->m_unexpected_block_readsize; } int32_t scap_enable_simpledriver_mode(scap_t* handle) { // // Not supported on files // if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "setting simpledriver mode not supported on this scap mode"); return SCAP_FAILURE; } #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else if(handle->m_bpf) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "setting simpledriver mode not supported on bpf"); ASSERT(false); return SCAP_FAILURE; } else { if(ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_SET_SIMPLE_MODE)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_enable_simpledriver_mode failed"); ASSERT(false); return SCAP_FAILURE; } } return SCAP_SUCCESS; #endif } int32_t scap_get_n_tracepoint_hit(scap_t* handle, long* ret) { int ioctl_ret = 0; // // Not supported on files // if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "getting n_tracepoint_hit not supported on this scap mode"); return SCAP_FAILURE; } #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else if(handle->m_bpf) { return scap_bpf_get_n_tracepoint_hit(handle, ret); } else { ioctl_ret = ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_GET_N_TRACEPOINT_HIT, ret); if(ioctl_ret != 0) { if(errno == ENOTTY) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_get_n_tracepoint_hit failed, ioctl not supported"); } else { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_get_n_tracepoint_hit failed (%s)", scap_strerror(handle, errno)); } ASSERT(false); return SCAP_FAILURE; } } return SCAP_SUCCESS; #endif } #ifdef CYGWING_AGENT wh_t* scap_get_wmi_handle(scap_t* handle) { return handle->m_whh; } #endif const char *scap_get_bpf_probe_from_env() { return getenv(SYSDIG_BPF_PROBE_ENV); } bool scap_get_bpf_enabled(scap_t *handle) { if(handle) { return handle->m_bpf; } return false; } int32_t scap_suppress_events_comm(scap_t *handle, const char *comm) { // If the comm is already present in the list, do nothing uint32_t i; for(i=0; im_num_suppressed_comms; i++) { if(strcmp(handle->m_suppressed_comms[i], comm) == 0) { return SCAP_SUCCESS; } } if(handle->m_num_suppressed_comms >= SCAP_MAX_SUPPRESSED_COMMS) { return SCAP_FAILURE; } handle->m_num_suppressed_comms++; handle->m_suppressed_comms = (char **) realloc(handle->m_suppressed_comms, handle->m_num_suppressed_comms * sizeof(char *)); handle->m_suppressed_comms[handle->m_num_suppressed_comms-1] = strdup(comm); return SCAP_SUCCESS; } bool scap_check_suppressed_tid(scap_t *handle, int64_t tid) { scap_tid *stid; HASH_FIND_INT64(handle->m_suppressed_tids, &tid, stid); return (stid != NULL); } int32_t scap_set_fullcapture_port_range(scap_t* handle, uint16_t range_start, uint16_t range_end) { // // Not supported on files // if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_set_fullcapture_port_range not supported on this scap mode"); return SCAP_FAILURE; } #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else if(handle->m_bpf) { return scap_bpf_set_fullcapture_port_range(handle, range_start, range_end); } else { // // Encode the port range // uint32_t arg = (range_end << 16) + range_start; // // Beam the value down to the module // if(ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_SET_FULLCAPTURE_PORT_RANGE, arg)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_set_fullcapture_port_range failed"); ASSERT(false); return SCAP_FAILURE; } { uint32_t j; // // Force a flush of the read buffers, so we don't capture events with the old snaplen // for(j = 0; j < handle->m_ndevs; j++) { scap_readbuf(handle, j, &handle->m_devs[j].m_sn_next_event, &handle->m_devs[j].m_sn_len); handle->m_devs[j].m_sn_len = 0; } } } return SCAP_SUCCESS; #endif } int32_t scap_set_statsd_port(scap_t* const handle, const uint16_t port) { // // Not supported on files // if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_set_statsd_port not supported on this scap mode"); return SCAP_FAILURE; } #if !defined(HAS_CAPTURE) || defined(CYGWING_AGENT) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else if(handle->m_bpf) { return scap_bpf_set_statsd_port(handle, port); } else { // // Beam the value down to the module // if(ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_SET_STATSD_PORT, port)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_set_statsd_port: ioctl failed"); ASSERT(false); return SCAP_FAILURE; } { uint32_t j; // // Force a flush of the read buffers, so we don't // capture events with the old snaplen // for(j = 0; j < handle->m_ndevs; j++) { scap_readbuf(handle, j, &handle->m_devs[j].m_sn_next_event, &handle->m_devs[j].m_sn_len); handle->m_devs[j].m_sn_len = 0; } } } return SCAP_SUCCESS; #endif } sysdig-0.26.4/userspace/libscap/scap.def000066400000000000000000000020601352731327100201240ustar00rootroot00000000000000LIBRARY scap EXPORTS scap_open_live scap_open_offline scap_open_offline_fd scap_open scap_close scap_get_os_platform scap_get_ndevs scap_getlasterr scap_next scap_event_getlen scap_event_get_ts scap_dump_open scap_dump_open_fd scap_dump_close scap_dump_get_offset scap_dump_flush scap_dump_ftell scap_dump scap_event_reset_count scap_event_get_num scap_get_proc_table scap_event_getinfo scap_stop_capture scap_get_ifaddr_list scap_get_stats scap_get_event_info_table scap_get_syscall_info_table scap_proc_get scap_proc_free scap_start_capture scap_get_machine_info scap_stop_dropping_mode scap_start_dropping_mode scap_get_user_list scap_free_userlist scap_set_snaplen scap_get_readfile_offset scap_clear_eventmask scap_set_eventmask scap_unset_eventmask scap_number_of_bytes_to_write scap_event_get_dump_flags scap_enable_dynamic_snaplen scap_disable_dynamic_snaplen scap_proc_free_table scap_is_thread_alive scap_get_threadlist scap_get_host_root scap_ftell scap_fseek sysdig-0.26.4/userspace/libscap/scap.h000066400000000000000000001016301352731327100176200ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #ifdef __cplusplus extern "C" { #endif /*! \mainpage libscap documentation \section Introduction libscap is the low-level sysdig component that exports the following functionality: - live capture control (start/stop/pause...) - trace file management - event retrieval - extraction of system state from /proc This manual includes the following sections: - \ref scap_defs - \ref scap_functs */ /////////////////////////////////////////////////////////////////////////////// // Public structs and defines /////////////////////////////////////////////////////////////////////////////// /** @defgroup scap_defs public definitions and structures * @{ */ // // Forward declarations // typedef struct scap scap_t; typedef struct ppm_evt_hdr scap_evt; struct iovec; // // Core types // #include "uthash.h" #include "../common/sysdig_types.h" #include "../../driver/ppm_events_public.h" // // Return types // #define SCAP_SUCCESS 0 #define SCAP_FAILURE 1 #define SCAP_TIMEOUT -1 #define SCAP_ILLEGAL_INPUT 3 #define SCAP_NOTFOUND 4 #define SCAP_INPUT_TOO_SMALL 5 #define SCAP_EOF 6 #define SCAP_UNEXPECTED_BLOCK 7 #define SCAP_VERSION_MISMATCH 8 #define SCAP_NOT_SUPPORTED 9 // // Last error string size for scap_open_live() // #define SCAP_LASTERR_SIZE 256 /*! \brief Statistics about an in progress capture */ typedef struct scap_stats { uint64_t n_evts; ///< Total number of events that were received by the driver. uint64_t n_drops; ///< Number of dropped events. uint64_t n_drops_buffer; ///< Number of dropped events caused by full buffer. uint64_t n_drops_pf; ///< Number of dropped events caused by invalid memory access. uint64_t n_drops_bug; ///< Number of dropped events caused by an invalid condition in the kernel instrumentation. uint64_t n_preemptions; ///< Number of preemptions. uint64_t n_suppressed; ///< Number of events skipped due to the tid being in a set of suppressed tids uint64_t n_tids_suppressed; ///< Number of threads currently being suppressed }scap_stats; /*! \brief Information about the parameter of an event */ typedef struct evt_param_info { const char* name; ///< The event name. uint32_t type; ///< The event type. See the ppm_event_type enum in driver/ppm_events_public.h uint32_t len; ///< The event total length. char* val; ///< The event data. }evt_param_info; #define SCAP_MAX_PATH_SIZE 1024 #define SCAP_MAX_ARGS_SIZE 4096 #define SCAP_MAX_ENV_SIZE 4096 #define SCAP_MAX_CGROUPS_SIZE 4096 #define SCAP_MAX_SUPPRESSED_COMMS 32 /*! \brief File Descriptor type */ typedef enum scap_fd_type { SCAP_FD_UNINITIALIZED = -1, SCAP_FD_UNKNOWN = 0, SCAP_FD_FILE = 1, SCAP_FD_DIRECTORY = 2, SCAP_FD_IPV4_SOCK = 3, SCAP_FD_IPV6_SOCK = 4, SCAP_FD_IPV4_SERVSOCK = 5, SCAP_FD_IPV6_SERVSOCK = 6, SCAP_FD_FIFO = 7, SCAP_FD_UNIX_SOCK = 8, SCAP_FD_EVENT = 9, SCAP_FD_UNSUPPORTED = 10, SCAP_FD_SIGNALFD = 11, SCAP_FD_EVENTPOLL = 12, SCAP_FD_INOTIFY = 13, SCAP_FD_TIMERFD = 14, SCAP_FD_NETLINK = 15, SCAP_FD_FILE_V2 = 16 }scap_fd_type; /*! \brief Socket type / transport protocol */ typedef enum scap_l4_proto { SCAP_L4_UNKNOWN = 0, ///< unknown protocol, likely caused by some parsing problem SCAP_L4_NA = 1, ///< protocol not available, because the fd is not a socket SCAP_L4_TCP = 2, SCAP_L4_UDP = 3, SCAP_L4_ICMP = 4, SCAP_L4_RAW = 5, ///< Raw socket }scap_l4_proto; /*! \brief Information about a file descriptor */ typedef struct scap_fdinfo { int64_t fd; ///< The FD number, which uniquely identifies this file descriptor. uint64_t ino; ///< For unix sockets, the inode. scap_fd_type type; ///< This file descriptor's type. union { struct { uint32_t sip; ///< Source IP uint32_t dip; ///< Destination IP uint16_t sport; ///< Source port uint16_t dport; ///< Destination port uint8_t l4proto; ///< Transport protocol. See \ref scap_l4_proto. } ipv4info; ///< Information specific to IPv4 sockets struct { uint32_t sip[4]; ///< Source IP uint32_t dip[4]; ///< Destination IP uint16_t sport; ///< Source Port uint16_t dport; ///< Destination Port uint8_t l4proto; ///< Transport protocol. See \ref scap_l4_proto. } ipv6info; ///< Information specific to IPv6 sockets struct { uint32_t ip; ///< Local IP uint16_t port; ///< Local Port uint8_t l4proto; ///< Transport protocol. See \ref scap_l4_proto. } ipv4serverinfo; ///< Information specific to IPv4 server sockets, e.g. sockets used for bind(). struct { uint32_t ip[4]; ///< Local IP uint16_t port; ///< Local Port uint8_t l4proto; ///< Transport protocol. See \ref scap_l4_proto. } ipv6serverinfo; ///< Information specific to IPv6 server sockets, e.g. sockets used for bind(). struct { uint64_t source; ///< Source socket endpoint uint64_t destination; ///< Destination socket endpoint char fname[SCAP_MAX_PATH_SIZE]; ///< Name associated to this unix socket } unix_socket_info; ///< Information specific to unix sockets struct { uint32_t open_flags; ///< Flags associated with the file char fname[SCAP_MAX_PATH_SIZE]; ///< Name associated to this file uint32_t dev; ///< Major/minor number of the device containing this file } regularinfo; ///< Information specific to regular files char fname[SCAP_MAX_PATH_SIZE]; ///< The name for file system FDs }info; UT_hash_handle hh; ///< makes this structure hashable }scap_fdinfo; /*! \brief Process information */ typedef struct scap_threadinfo { uint64_t tid; ///< The thread/task id. uint64_t pid; ///< The id of the process containing this thread. In single thread processes, this is equal to tid. uint64_t ptid; ///< The id of the thread that created this thread. uint64_t sid; ///< The session id of the process containing this thread. uint64_t vpgid; ///< The process group of this thread, as seen from its current pid namespace char comm[SCAP_MAX_PATH_SIZE+1]; ///< Command name (e.g. "top") char exe[SCAP_MAX_PATH_SIZE+1]; ///< argv[0] (e.g. "sshd: user@pts/4") char exepath[SCAP_MAX_PATH_SIZE+1]; ///< full executable path char args[SCAP_MAX_ARGS_SIZE+1]; ///< Command line arguments (e.g. "-d1") uint16_t args_len; ///< Command line arguments length char env[SCAP_MAX_ENV_SIZE+1]; ///< Environment uint16_t env_len; ///< Environment length char cwd[SCAP_MAX_PATH_SIZE+1]; ///< The current working directory int64_t fdlimit; ///< The maximum number of files this thread is allowed to open uint32_t flags; ///< the process flags. uint32_t uid; ///< user id uint32_t gid; ///< group id uint32_t vmsize_kb; ///< total virtual memory (as kb) uint32_t vmrss_kb; ///< resident non-swapped memory (as kb) uint32_t vmswap_kb; ///< swapped memory (as kb) uint64_t pfmajor; ///< number of major page faults since start uint64_t pfminor; ///< number of minor page faults since start int64_t vtid; int64_t vpid; char cgroups[SCAP_MAX_CGROUPS_SIZE]; uint16_t cgroups_len; char root[SCAP_MAX_PATH_SIZE+1]; int filtered_out; ///< nonzero if this entry should not be saved to file scap_fdinfo* fdlist; ///< The fd table for this process uint64_t clone_ts; int32_t tty; int32_t loginuid; ///< loginuid (auid) UT_hash_handle hh; ///< makes this structure hashable }scap_threadinfo; /*! \brief Mount information */ typedef struct { uint64_t mount_id; ///< mount id from /proc/self/mountinfo uint32_t dev; ///< device number UT_hash_handle hh; ///< makes this structure hashable } scap_mountinfo; typedef void (*proc_entry_callback)(void* context, scap_t* handle, int64_t tid, scap_threadinfo* tinfo, scap_fdinfo* fdinfo); /*! \brief Arguments for scap_open */ typedef enum { /*! * Default value that mostly exists so that sinsp can have a valid value * before it is initialized. */ SCAP_MODE_NONE = 0, /*! * Read system call data from a capture file. */ SCAP_MODE_CAPTURE, /*! * Read system call data from the underlying operating system. */ SCAP_MODE_LIVE, /*! * Do not read system call data. If next is called, a dummy event is * returned. */ SCAP_MODE_NODRIVER } scap_mode_t; typedef struct scap_open_args { scap_mode_t mode; int fd; // If non-zero, will be used instead of fname. const char* fname; ///< The name of the file to open. NULL for live captures. proc_entry_callback proc_callback; ///< Callback to be invoked for each thread/fd that is extracted from /proc, or NULL if no callback is needed. void* proc_callback_context; ///< Opaque pointer that will be included in the calls to proc_callback. Ignored if proc_callback is NULL. bool import_users; ///< true if the user list should be created when opening the capture. uint64_t start_offset; ///< Used to start reading a capture file from an arbitrary offset. This is leveraged when opening merged files. const char *bpf_probe; ///< The name of the BPF probe to open. If NULL, the kernel driver will be used. const char *suppressed_comms[SCAP_MAX_SUPPRESSED_COMMS]; ///< A list of processes (comm) for which no // events should be returned, with a trailing NULL value. // You can provide additional comm // values via scap_suppress_events_comm(). }scap_open_args; // // The follwing stuff is byte aligned because we save it to disk. // #if defined _MSC_VER #pragma pack(push) #pragma pack(1) #elif defined __sun #pragma pack(1) #else #pragma pack(push, 1) #endif /*! \brief Machine information */ typedef struct _scap_machine_info { uint32_t num_cpus; ///< Number of processors uint64_t memory_size_bytes; ///< Physical memory size uint64_t max_pid; ///< Highest PID number on this machine char hostname[128]; ///< The machine hostname uint64_t reserved1; ///< reserved for future use uint64_t reserved2; ///< reserved for future use uint64_t reserved3; ///< reserved for future use uint64_t reserved4; ///< reserved for future use }scap_machine_info; #define SCAP_IPV6_ADDR_LEN 16 /*! \brief Interface address type */ typedef enum scap_ifinfo_type { SCAP_II_UNKNOWN = 0, SCAP_II_IPV4 = 1, SCAP_II_IPV6 = 2, SCAP_II_IPV4_NOLINKSPEED = 3, SCAP_II_IPV6_NOLINKSPEED = 4, }scap_ifinfo_type; /*! \brief IPv4 interface address information */ typedef struct scap_ifinfo_ipv4 { // NB: new fields must be appended uint16_t type; ///< Interface type uint16_t ifnamelen; uint32_t addr; ///< Interface address uint32_t netmask; ///< Interface netmask uint32_t bcast; ///< Interface broadcast address uint64_t linkspeed; ///< Interface link speed char ifname[SCAP_MAX_PATH_SIZE]; ///< interface name (e.g. "eth0") }scap_ifinfo_ipv4; /*! \brief For backward compatibility only */ typedef struct scap_ifinfo_ipv4_nolinkspeed { uint16_t type; uint16_t ifnamelen; uint32_t addr; uint32_t netmask; uint32_t bcast; char ifname[SCAP_MAX_PATH_SIZE]; }scap_ifinfo_ipv4_nolinkspeed; /*! \brief IPv6 interface address information */ typedef struct scap_ifinfo_ipv6 { // NB: new fields must be appended uint16_t type; uint16_t ifnamelen; char addr[SCAP_IPV6_ADDR_LEN]; ///< Interface address char netmask[SCAP_IPV6_ADDR_LEN]; ///< Interface netmask char bcast[SCAP_IPV6_ADDR_LEN]; ///< Interface broadcast address uint64_t linkspeed; ///< Interface link speed char ifname[SCAP_MAX_PATH_SIZE]; ///< interface name (e.g. "eth0") }scap_ifinfo_ipv6; /*! \brief For backword compatibility only */ typedef struct scap_ifinfo_ipv6_nolinkspeed { uint16_t type; uint16_t ifnamelen; char addr[SCAP_IPV6_ADDR_LEN]; char netmask[SCAP_IPV6_ADDR_LEN]; char bcast[SCAP_IPV6_ADDR_LEN]; char ifname[SCAP_MAX_PATH_SIZE]; }scap_ifinfo_ipv6_nolinkspeed; #if defined __sun #pragma pack() #else #pragma pack(pop) #endif /*! \brief List of the machine network interfaces */ typedef struct scap_addrlist { uint32_t n_v4_addrs; ///< Number of IPv4 addresses uint32_t n_v6_addrs; ///< Number of IPv6 addresses uint32_t totlen; ///< For internal use scap_ifinfo_ipv4* v4list; ///< List of IPv4 Addresses scap_ifinfo_ipv6* v6list; ///< List of IPv6 Addresses }scap_addrlist; #define MAX_CREDENTIALS_STR_LEN 256 #define USERBLOCK_TYPE_USER 0 #define USERBLOCK_TYPE_GROUP 1 /*! \brief Information about one of the machine users */ typedef struct scap_userinfo { uint32_t uid; ///< User ID uint32_t gid; ///< Group ID char name[MAX_CREDENTIALS_STR_LEN]; ///< Username char homedir[SCAP_MAX_PATH_SIZE]; ///< Home directory char shell[SCAP_MAX_PATH_SIZE]; ///< Shell program }scap_userinfo; /*! \brief Information about one of the machine user groups */ typedef struct scap_groupinfo { uint32_t gid; ///< Group ID char name[MAX_CREDENTIALS_STR_LEN]; ///< Group name }scap_groupinfo; /*! \brief List of the machine users and groups */ typedef struct scap_userlist { uint32_t nusers; ///< Number of users uint32_t ngroups; ///< Number of groups uint32_t totsavelen; ///< For internal use scap_userinfo* users; ///< User list scap_groupinfo* groups; ///< Group list }scap_userlist; // // Misc definitions // /*! \brief The OS on which the capture was made */ typedef enum scap_os_platform { SCAP_PFORM_UNKNOWN = 0, SCAP_PFORM_LINUX_I386 = 1, SCAP_PFORM_LINUX_X64 = 2, SCAP_PFORM_WINDOWS_I386 = 3, SCAP_PFORM_WINDOWS_X64 = 4, }scap_os_platform; /*! \brief Indicates if an event is an enter one or an exit one */ typedef enum event_direction { SCAP_ED_IN = 0, SCAP_ED_OUT = 1 }event_direction; /*! \brief Indicates the compression type used when writing a tracefile */ typedef enum compression_mode { SCAP_COMPRESSION_NONE = 0, SCAP_COMPRESSION_GZIP = 1 }compression_mode; /*! \brief Flags for scap_dump */ typedef enum scap_dump_flags { SCAP_DF_NONE = 0, SCAP_DF_STATE_ONLY = 1, ///< The event should be used for state update but it should ///< not be shown to the user SCAP_DF_TRACER = (1 << 1) ///< This event is a tracer }scap_dump_flags; typedef struct scap_dumper scap_dumper_t; /*! \brief System call description struct. */ struct ppm_syscall_desc { enum ppm_event_category category; /**< System call category. */ enum ppm_event_flags flags; char *name; /**< System call name, e.g. 'open'. */ }; /*@}*/ /////////////////////////////////////////////////////////////////////////////// // Structs and defines used internally /////////////////////////////////////////////////////////////////////////////// #define IN #define OUT /////////////////////////////////////////////////////////////////////////////// // API functions /////////////////////////////////////////////////////////////////////////////// /** @defgroup scap_functs API Functions * @{ */ /*! \brief Start a live event capture. \param error Pointer to a buffer that will contain the error string in case the function fails. The buffer must have size SCAP_LASTERR_SIZE. \param rc Integer pointer that will contain the scap return code in case the function fails. \return The capture instance handle in case of success. NULL in case of failure. */ scap_t* scap_open_live(char *error, int32_t *rc); /*! \brief Start an event capture from file. \param fname The name of the file to open. \param error Pointer to a buffer that will contain the error string in case the function fails. The buffer must have size SCAP_LASTERR_SIZE. \param rc Integer pointer that will contain the scap return code in case the function fails. \return The capture instance handle in case of success. NULL in case of failure. */ scap_t* scap_open_offline(const char* fname, char *error, int32_t *rc); /*! \brief Start an event capture from an already opened file descriptor. \param fd The fd to use. \param error Pointer to a buffer that will contain the error string in case the function fails. The buffer must have size SCAP_LASTERR_SIZE. \param rc Integer pointer that will contain the scap return code in case the function fails. \return The capture instance handle in case of success. NULL in case of failure. */ scap_t* scap_open_offline_fd(int fd, char *error, int32_t *rc); /*! \brief Advanced function to start a capture. \param args a \ref scap_open_args structure containing the open paraneters. \param error Pointer to a buffer that will contain the error string in case the function fails. The buffer must have size SCAP_LASTERR_SIZE. \param rc Integer pointer that will contain the scap return code in case the function fails. \return The capture instance handle in case of success. NULL in case of failure. */ scap_t* scap_open(scap_open_args args, char *error, int32_t *rc); /*! \brief Close a capture handle. \param handle Handle to the capture instance. */ void scap_close(scap_t* handle); /*! \brief Retrieve the OS platform for the given capture handle. \param handle Handle to the capture instance. \return The type of operating system on which the capture was made. \note For live handles, the return value indicates the current local OS. For offline handles, the return value indicates the OS where the data was originally captured. */ scap_os_platform scap_get_os_platform(scap_t* handle); /*! \brief Return a string with the last error that happened on the given capture. */ const char* scap_getlasterr(scap_t* handle); /*! \brief Get the next event from the from the given capture instance \param handle Handle to the capture instance. \param pevent User-provided event pointer that will be initialized with address of the event. \param pcpuid User-provided event pointer that will be initialized with the ID if the CPU where the event was captured. \return SCAP_SUCCESS if the call is successful and pevent and pcpuid contain valid data. SCAP_TIMEOUT in case the read timeout expired and no event is available. SCAP_EOF when the end of an offline capture is reached. On Failure, SCAP_FAILURE is returned and scap_getlasterr() can be used to obtain the cause of the error. */ int32_t scap_next(scap_t* handle, OUT scap_evt** pevent, OUT uint16_t* pcpuid); /*! \brief Get the length of an event \param e pointer to an event returned by \ref scap_next. \return The event length in bytes. */ uint32_t scap_event_getlen(scap_evt* e); /*! \brief Get the timestamp of an event \param e pointer to an event returned by \ref scap_next. \return The event timestamp, in nanoseconds since epoch. */ uint64_t scap_event_get_ts(scap_evt* e); /*! \brief Get the number of events that have been captured from the given capture instance \param handle Handle to the capture instance. \return The total number of events. */ uint64_t scap_event_get_num(scap_t* handle); /*! \brief Reset the event count to 0. \param handle Handle to the capture instance. */ void scap_event_reset_count(scap_t* handle); /*! \brief Return the meta-information describing the given event \param e pointer to an event returned by \ref scap_next. \return The pointer to the the event table entry for the given event. */ const struct ppm_event_info* scap_event_getinfo(scap_evt* e); /*! \brief Return the dump flags for the last event received from this handle \param handle Handle to the capture instance. \return The flags if the capture is offline, 0 if the capture is live. */ uint32_t scap_event_get_dump_flags(scap_t* handle); /*! \brief Return the current offset in the file opened by scap_open_offline(), or -1 if this is a live capture. \param handle Handle to the capture instance. */ int64_t scap_get_readfile_offset(scap_t* handle); /*! \brief Open a trace file for writing \param handle Handle to the capture instance. \param fname The name of the trace file. \return Dump handle that can be used to identify this specific dump instance. */ scap_dumper_t* scap_dump_open(scap_t *handle, const char *fname, compression_mode compress, bool skip_proc_scan); /*! \brief Open a trace file for writing, using the provided fd. \param handle Handle to the capture instance. \param fd A file descriptor to which the dumper will write \return Dump handle that can be used to identify this specific dump instance. */ scap_dumper_t* scap_dump_open_fd(scap_t *handle, int fd, compression_mode compress, bool skip_proc_scan); /*! \brief Close a trace file. \param d The dump handle, returned by \ref scap_dump_open */ void scap_dump_close(scap_dumper_t *d); /*! \brief Return the current size of a trace file. \param d The dump handle, returned by \ref scap_dump_open \return The current size of the dump file pointed by d. */ int64_t scap_dump_get_offset(scap_dumper_t *d); /*! \brief Return the position for the next write to a trace file. This uses gztell, while scap_dump_get_offset uses gzoffset. \param d The dump handle, returned by \ref scap_dump_open \return The next write position. */ int64_t scap_dump_ftell(scap_dumper_t *d); /*! \brief Flush all pending output into the file. \param d The dump handle, returned by \ref scap_dump_open */ void scap_dump_flush(scap_dumper_t *d); /*! \brief Tell how many bytes would be written (a dry run of scap_dump) \param e pointer to an event returned by \ref scap_next. \param cpuid The cpu from which the event was captured. Returned by \ref scap_next. \param bytes The number of bytes to write \return SCAP_SUCCESS if the call is successful. On Failure, SCAP_FAILURE is returned and scap_getlasterr() can be used to obtain the cause of the error. */ int32_t scap_number_of_bytes_to_write(scap_evt *e, uint16_t cpuid, int32_t* bytes); /*! \brief Write an event to a trace file \param handle Handle to the capture instance. \param d The dump handle, returned by \ref scap_dump_open \param e pointer to an event returned by \ref scap_next. \param cpuid The cpu from which the event was captured. Returned by \ref scap_next. \param flags The event flags. 0 means no flags. \return SCAP_SUCCESS if the call is successful. On Failure, SCAP_FAILURE is returned and scap_getlasterr() can be used to obtain the cause of the error. */ int32_t scap_dump(scap_t *handle, scap_dumper_t *d, scap_evt* e, uint16_t cpuid, uint32_t flags); /*! \brief Get the process list for the given capture instance \param handle Handle to the capture instance. \return Pointer to the process list. for live captures, the process list is created when the capture starts by scanning the proc file system. For offline captures, it is retrieved from the file. The process list contains information about the processes that were already open when the capture started. It can be traversed with uthash, using the following syntax: \code scap_threadinfo *pi; scap_threadinfo *tpi; scap_threadinfo *table = scap_get_proc_table(phandle); HASH_ITER(hh, table, pi, tpi) { // do something with pi } \endcode Refer to the documentation of the \ref scap_threadinfo struct for details about its content. */ scap_threadinfo* scap_get_proc_table(scap_t* handle); /*! \brief Return the capture statistics for the given capture handle. \param handle Handle to the capture instance. \param stats Pointer to a \ref scap_stats structure that will be filled with the statistics. \return SCAP_SECCESS if the call is successful. On Failure, SCAP_FAILURE is returned and scap_getlasterr() can be used to obtain the cause of the error. */ int32_t scap_get_stats(scap_t* handle, OUT scap_stats* stats); /*! \brief This function can be used to temporarily interrupt event capture. \param handle Handle to the capture that will be stopped. \return SCAP_SUCCESS if the call is successful. On Failure, SCAP_FAILURE is returned and scap_getlasterr() can be used to obtain the cause of the error. */ int32_t scap_stop_capture(scap_t* handle); /*! \brief Start capture the events, if it was stopped with \ref scap_stop_capture. \param handle Handle to the capture that will be started. \return SCAP_SUCCESS if the call is successful. On Failure, SCAP_FAILURE is returned and scap_getlasterr() can be used to obtain the cause of the error. */ int32_t scap_start_capture(scap_t* handle); /*! \brief Return the list of the the user interfaces of the machine from which the events are being captured. \param handle Handle to the capture instance. \return The pointer to a \ref scap_addrlist structure containing the interface list, or NULL if the function fails. */ scap_addrlist* scap_get_ifaddr_list(scap_t* handle); /*! \brief Return the machine user and group lists \param handle Handle to the capture instance. \return The pointer to a \ref scap_userlist structure containing the user and group lists, or NULL if the function fails. */ scap_userlist* scap_get_user_list(scap_t* handle); /*! \brief Retrieve the table with the description of every event type that the capture driver supports. \return The pointer to a table of \ref scap_userlist entries, each of which describes one of the events that can come from the driver. The table contains PPM_EVENT_MAX entries, and the position of each entry in the table corresponds to its event ID. The ppm_event_info contains the full information necessary to decode an event coming from \ref scap_next. */ const struct ppm_event_info* scap_get_event_info_table(); /*! \brief Retrieve the table with the description of system call that the capture driver supports. \return The pointer to a table of \ref ppm_syscall_desc entries, each of which describes one of the events that can come from the driver. The table contains SYSCALL_TABLE_SIZE entries, and the position of each entry in the table corresponds to the system call ID. This table can be used to interpret the ID parameter of PPME_GENERIC_E and PPME_GENERIC_X. */ const struct ppm_syscall_desc* scap_get_syscall_info_table(); /*! \brief Get generic machine information \return The pointer to a \ref scap_machine_info structure containing the information. \note for live captures, the information is collected from the operating system. For offline captures, it comes from the capture file. */ const scap_machine_info* scap_get_machine_info(scap_t* handle); /*! \brief Set the capture snaplen, i.e. the maximum size an event parameter can reach before the driver starts truncating it. \param handle Handle to the capture instance. \param snaplen the snaplen for this capture instance, in bytes. \note This function can only be called for live captures. \note By default, the driver captures the first 80 bytes of the buffers coming from events like read, write, send, recv, etc. If you're not interested in payloads, smaller values will save capture buffer space and make capture files smaller. Conversely, big values should be used with care because they can easily generate huge capture files. */ int32_t scap_set_snaplen(scap_t* handle, uint32_t snaplen); /*! \brief Clear the event mask: no events will be passed to sysdig \param handle Handle to the capture instance. \note This function can only be called for live captures. */ int32_t scap_clear_eventmask(scap_t* handle); /*! \brief Set the event into the eventmask so that sysdig-based apps can receive the event. Useful for offloading operations such as evt.type=open \param handle Handle to the capture instance. \param event id (example PPME_SOCKET_BIND_X) \note This function can only be called for live captures. */ int32_t scap_set_eventmask(scap_t* handle, uint32_t event_id); /*! \brief Unset the event into the eventmask so that sysdig-based apps can no longer receive the event. It is the opposite of scap_set_eventmask \param handle Handle to the capture instance. \param event id (example PPME_SOCKET_BIND_X) \note This function can only be called for live captures. */ int32_t scap_unset_eventmask(scap_t* handle, uint32_t event_id); /*! \brief Get the root directory of the system. This usually changes if sysdig runs in a container, so that all the information for the host can be correctly extracted. */ const char* scap_get_host_root(); /*! \brief Get the process list. */ struct ppm_proclist_info* scap_get_threadlist(scap_t* handle); const char *scap_get_bpf_probe_from_env(); bool scap_get_bpf_enabled(scap_t* handle); /*! \brief stop returning events for all subsequently spawned processes with the provided comm, as well as their children. This includes fork()/clone()ed processes that might later exec to a different comm. returns SCAP_FAILURE if there are already MAX_SUPPRESSED_COMMS comm values, SCAP_SUCCESS otherwise. */ int32_t scap_suppress_events_comm(scap_t* handle, const char *comm); /*! \brief return whether the provided tid is currently being suppressed. */ bool scap_check_suppressed_tid(scap_t *handle, int64_t tid); /*@}*/ /////////////////////////////////////////////////////////////////////////////// // Non public functions /////////////////////////////////////////////////////////////////////////////// // // Return the number of event capture devices that the library is handling. Each processor // has its own event capture device. // uint32_t scap_get_ndevs(scap_t* handle); // Retrieve a buffer of events from one of the cpus extern int32_t scap_readbuf(scap_t* handle, uint32_t cpuid, OUT char** buf, OUT uint32_t* len); #ifdef PPM_ENABLE_SENTINEL // Get the sentinel at the beginning of the event uint32_t scap_event_get_sentinel_begin(scap_evt* e); #endif // Get the information about a process. // The returned pointer must be freed via scap_proc_free by the caller. struct scap_threadinfo* scap_proc_get(scap_t* handle, int64_t tid, bool scan_sockets); // Check if the given thread exists in ;proc bool scap_is_thread_alive(scap_t* handle, int64_t pid, int64_t tid, const char* comm); // like getpid() but returns the global PID even inside a container int32_t scap_getpid_global(scap_t* handle, int64_t* pid); struct scap_threadinfo *scap_proc_alloc(scap_t* handle); void scap_proc_free(scap_t* handle, struct scap_threadinfo* procinfo); void scap_dev_delete(scap_t* handle, scap_mountinfo* dev); int32_t scap_stop_dropping_mode(scap_t* handle); int32_t scap_start_dropping_mode(scap_t* handle, uint32_t sampling_ratio); int32_t scap_enable_dynamic_snaplen(scap_t* handle); int32_t scap_disable_dynamic_snaplen(scap_t* handle); void scap_proc_free_table(scap_t* handle); void scap_free_device_table(scap_t* handle); void scap_refresh_iflist(scap_t* handle); void scap_refresh_proc_table(scap_t* handle); void scap_set_refresh_proc_table_when_saving(scap_t* handle, bool refresh); uint64_t scap_ftell(scap_t *handle); void scap_fseek(scap_t *handle, uint64_t off); int32_t scap_enable_tracers_capture(scap_t* handle); int32_t scap_enable_page_faults(scap_t *handle); uint64_t scap_get_unexpected_block_readsize(scap_t* handle); int32_t scap_proc_add(scap_t* handle, uint64_t tid, scap_threadinfo* tinfo); int32_t scap_fd_add(scap_t *handle, scap_threadinfo* tinfo, uint64_t fd, scap_fdinfo* fdinfo); scap_dumper_t *scap_memory_dump_open(scap_t *handle, uint8_t* targetbuf, uint64_t targetbufsize); int32_t compr(uint8_t* dest, uint64_t* destlen, const uint8_t* source, uint64_t sourcelen, int level); uint8_t* scap_get_memorydumper_curpos(scap_dumper_t *d); int32_t scap_write_proc_fds(scap_t *handle, struct scap_threadinfo *tinfo, scap_dumper_t *d); int32_t scap_write_proclist_header(scap_t *handle, scap_dumper_t *d, uint32_t totlen); int32_t scap_write_proclist_trailer(scap_t *handle, scap_dumper_t *d, uint32_t totlen); int32_t scap_write_proclist_entry(scap_t *handle, scap_dumper_t *d, struct scap_threadinfo *tinfo, uint32_t len); // Variant of scap_write_proclist_entry where array-backed information // about the thread is provided separate from the scap_threadinfo // struct. int32_t scap_write_proclist_entry_bufs(scap_t *handle, scap_dumper_t *d, struct scap_threadinfo *tinfo, uint32_t len, const char *comm, const char *exe, const char *exepath, const struct iovec *args, int argscnt, const struct iovec *envs, int envscnt, const char *cwd, const struct iovec *cgroups, int cgroupscnt, const char *root); // Turn on processing only a subset syscalls. This is only appliable when scap // is in LIVE mode. int32_t scap_enable_simpledriver_mode(scap_t* handle); int32_t scap_get_n_tracepoint_hit(scap_t* handle, long* ret); #ifdef CYGWING_AGENT typedef struct wh_t wh_t; wh_t* scap_get_wmi_handle(scap_t* handle); #endif int32_t scap_set_fullcapture_port_range(scap_t* handle, uint16_t range_start, uint16_t range_end); /** * By default we have an expanded snaplen for the default statsd port. If the * statsd port is non-standard, communicate that port value to the kernel to * get the expanded snaplen for the correct port. */ int32_t scap_set_statsd_port(scap_t* handle, uint16_t port); #ifdef __cplusplus } #endif sysdig-0.26.4/userspace/libscap/scap.vcxproj000066400000000000000000000150551352731327100210710ustar00rootroot00000000000000 Debug Win32 Release Win32 {A45C5520-F4BA-480A-A7D3-EB1E9DDFBE6C} Win32Proj DynamicLibrary true v140 DynamicLibrary false v140 true $(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath);.;../../driver $(ProjectDir)\..\Debug\ $(ProjectDir)\..\Debug\ true $(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath);.;../../driver $(ProjectDir)\..\Release\ $(ProjectDir)\..\Release\ WIN32;_CRT_SECURE_NO_WARNINGS;PLATFORM_NAME="Windows";_DEBUG;_WINDOWS;_USRDLL;SCAP_EXPORTS;%(PreprocessorDefinitions) MultiThreadedDebugDLL Level3 ProgramDatabase Disabled ../../common;../../build/zlib-prefix/src/zlib MachineX86 true Console $(ProjectDir)\..\Debug\$(TargetName).lib scap.def $(SolutionDir)Debug\$(TargetName)$(TargetExt) $(OutDir)scap.pdb kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Ws2_32.lib;%(AdditionalDependencies);../../build/zlib-prefix/src/zlib/zdll.lib WIN32;_CRT_SECURE_NO_WARNINGS;PLATFORM_NAME="Windows";NDEBUG;_WINDOWS;_USRDLL;SCAP_EXPORTS;%(PreprocessorDefinitions) MultiThreadedDLL Level3 ProgramDatabase ../../common;../../build/zlib-prefix/src/zlib MachineX86 true Windows true true $(ProjectDir)\..\Release\$(TargetName).lib scap.def $(SolutionDir)\Release\$(TargetName)$(TargetExt) $(OutDir)scap.pdb kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Ws2_32.lib;%(AdditionalDependencies);../../build/zlib-prefix/src/zlib/zdll.lib sysdig-0.26.4/userspace/libscap/scap.vcxproj.filters000066400000000000000000000051511352731327100225340ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files sysdig-0.26.4/userspace/libscap/scap_bpf.c000066400000000000000000001101161352731327100204410ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "scap.h" #include "scap-int.h" #include "scap_bpf.h" #include "driver_config.h" #include "../../driver/bpf/types.h" #include "compat/misc.h" #include "compat/bpf.h" // // Some of this code is taken from the kernel samples under samples/bpf, // namely the parsing of the ELF objects, which is very tedious and not // worth reinventing from scratch. The code has been readapted and simplified // to tailor the sysdig use case. In the future, sysdig can fully switch to // libbpf, but at the moment is not very worth the effort considering the // subset of features needed. // struct bpf_map_data { int fd; size_t elf_offset; struct bpf_map_def def; }; static const int BUF_SIZE_PAGES = 2048; static const int BPF_LOG_SIZE = 1 << 18; #define FILLER_NAME_FN(x) #x, static const char *g_filler_names[PPM_FILLER_MAX] = { FILLER_LIST_MAPPER(FILLER_NAME_FN) }; #undef FILLER_NAME_FN static int32_t lookup_filler_id(const char *filler_name) { int j; for(j = 0; j < sizeof(g_filler_names) / sizeof(g_filler_names[0]); ++j) { if(strcmp(filler_name, g_filler_names[j]) == 0) { return j; } } return -1; } static int bpf_map_update_elem(int fd, const void *key, const void *value, uint64_t flags) { union bpf_attr attr; bzero(&attr, sizeof(attr)); attr.map_fd = fd; attr.key = (unsigned long) key; attr.value = (unsigned long) value; attr.flags = flags; return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); } static int bpf_map_lookup_elem(int fd, const void *key, void *value) { union bpf_attr attr; bzero(&attr, sizeof(attr)); attr.map_fd = fd; attr.key = (unsigned long) key; attr.value = (unsigned long) value; return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); } static int bpf_map_create(enum bpf_map_type map_type, int key_size, int value_size, int max_entries, uint32_t map_flags) { union bpf_attr attr; bzero(&attr, sizeof(attr)); attr.map_type = map_type; attr.key_size = key_size; attr.value_size = value_size; attr.max_entries = max_entries; attr.map_flags = map_flags; return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); } static int bpf_load_program(const struct bpf_insn *insns, enum bpf_prog_type type, size_t insns_cnt, char *log_buf, size_t log_buf_sz) { union bpf_attr attr; int fd; bzero(&attr, sizeof(attr)); attr.prog_type = type; attr.insn_cnt = (uint32_t) insns_cnt; attr.insns = (unsigned long) insns; attr.license = (unsigned long) "GPL"; attr.log_buf = (unsigned long) NULL; attr.log_size = 0; attr.log_level = 0; fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); if(fd >= 0 || !log_buf || !log_buf_sz) { return fd; } attr.log_buf = (unsigned long) log_buf; attr.log_size = log_buf_sz; attr.log_level = 1; log_buf[0] = 0; return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); } static int bpf_raw_tracepoint_open(const char *name, int prog_fd) { union bpf_attr attr; bzero(&attr, sizeof(attr)); attr.raw_tracepoint.name = (unsigned long) name; attr.raw_tracepoint.prog_fd = prog_fd; return sys_bpf(BPF_RAW_TRACEPOINT_OPEN, &attr, sizeof(attr)); } static int32_t get_elf_section(Elf *elf, int i, GElf_Ehdr *ehdr, char **shname, GElf_Shdr *shdr, Elf_Data **data) { Elf_Scn *scn = elf_getscn(elf, i); if(!scn) { return SCAP_FAILURE; } if(gelf_getshdr(scn, shdr) != shdr) { return SCAP_FAILURE; } *shname = elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name); if(!*shname || !shdr->sh_size) { return SCAP_FAILURE; } *data = elf_getdata(scn, 0); if(!*data || elf_getdata(scn, *data) != NULL) { return SCAP_FAILURE; } return SCAP_SUCCESS; } static int cmp_symbols(const void *l, const void *r) { const GElf_Sym *lsym = (const GElf_Sym *)l; const GElf_Sym *rsym = (const GElf_Sym *)r; if(lsym->st_value < rsym->st_value) { return -1; } else if(lsym->st_value > rsym->st_value) { return 1; } else { return 0; } } static int32_t load_elf_maps_section(scap_t *handle, struct bpf_map_data *maps, int maps_shndx, Elf *elf, Elf_Data *symbols, int strtabidx, int *nr_maps) { Elf_Data *data_maps; GElf_Sym *sym; Elf_Scn *scn; int i; scn = elf_getscn(elf, maps_shndx); if(scn) { data_maps = elf_getdata(scn, NULL); } if(!scn || !data_maps) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Failed to get Elf_Data from maps section %d", maps_shndx); return SCAP_FAILURE; } *nr_maps = 0; sym = calloc(BPF_MAPS_MAX + 1, sizeof(GElf_Sym)); for(i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) { ASSERT(*nr_maps < BPF_MAPS_MAX + 1); if(!gelf_getsym(symbols, i, &sym[*nr_maps])) { continue; } if(sym[*nr_maps].st_shndx != maps_shndx) { continue; } (*nr_maps)++; } qsort(sym, *nr_maps, sizeof(GElf_Sym), cmp_symbols); ASSERT(data_maps->d_size / *nr_maps == sizeof(struct bpf_map_def)); for(i = 0; i < *nr_maps; i++) { struct bpf_map_def *def; size_t offset; offset = sym[i].st_value; def = (struct bpf_map_def *)(data_maps->d_buf + offset); maps[i].elf_offset = offset; memcpy(&maps[i].def, def, sizeof(struct bpf_map_def)); } free(sym); return SCAP_SUCCESS; } static int32_t load_maps(scap_t *handle, struct bpf_map_data *maps, int nr_maps) { int j; for(j = 0; j < nr_maps; ++j) { if(j == SYSDIG_PERF_MAP || j == SYSDIG_LOCAL_STATE_MAP || j == SYSDIG_FRAME_SCRATCH_MAP || j == SYSDIG_TMP_SCRATCH_MAP) { maps[j].def.max_entries = handle->m_ncpus; } handle->m_bpf_map_fds[j] = bpf_map_create(maps[j].def.type, maps[j].def.key_size, maps[j].def.value_size, maps[j].def.max_entries, maps[j].def.map_flags); maps[j].fd = handle->m_bpf_map_fds[j]; if(handle->m_bpf_map_fds[j] < 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can't create map: %s", scap_strerror(handle, errno)); return SCAP_FAILURE; } if(maps[j].def.type == BPF_MAP_TYPE_PROG_ARRAY) { handle->m_bpf_prog_array_map_idx = j; } } return SCAP_SUCCESS; } static int32_t parse_relocations(scap_t *handle, Elf_Data *data, Elf_Data *symbols, GElf_Shdr *shdr, struct bpf_insn *insn, struct bpf_map_data *maps, int nr_maps) { int nrels; int i; nrels = shdr->sh_size / shdr->sh_entsize; for(i = 0; i < nrels; i++) { GElf_Sym sym; GElf_Rel rel; unsigned int insn_idx; bool match = false; int map_idx; gelf_getrel(data, i, &rel); insn_idx = rel.r_offset / sizeof(struct bpf_insn); gelf_getsym(symbols, GELF_R_SYM(rel.r_info), &sym); if(insn[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid relocation for insn[%d].code 0x%x", insn_idx, insn[insn_idx].code); return SCAP_FAILURE; } insn[insn_idx].src_reg = BPF_PSEUDO_MAP_FD; for(map_idx = 0; map_idx < nr_maps; map_idx++) { if(maps[map_idx].elf_offset == sym.st_value) { match = true; break; } } if(match) { insn[insn_idx].imm = maps[map_idx].fd; } else { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid relocation for insn[%d] no map_data match\n", insn_idx); return SCAP_FAILURE; } } return SCAP_SUCCESS; } static int32_t load_tracepoint(scap_t* handle, const char *event, struct bpf_insn *prog, int size) { struct perf_event_attr attr = {}; enum bpf_prog_type program_type; size_t insns_cnt; char buf[256]; bool raw_tp; int efd; int err; int fd; int id; insns_cnt = size / sizeof(struct bpf_insn); attr.type = PERF_TYPE_TRACEPOINT; attr.sample_type = PERF_SAMPLE_RAW; attr.sample_period = 1; attr.wakeup_events = 1; char *error = malloc(BPF_LOG_SIZE); if(!error) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "malloc(BPF_LOG_BUF_SIZE)"); return SCAP_FAILURE; } if(memcmp(event, "raw_tracepoint/", sizeof("raw_tracepoint/") - 1) == 0) { raw_tp = true; program_type = BPF_PROG_TYPE_RAW_TRACEPOINT; event += sizeof("raw_tracepoint/") - 1; } else { raw_tp = false; program_type = BPF_PROG_TYPE_TRACEPOINT; event += sizeof("tracepoint/") - 1; } if(*event == 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "event name cannot be empty"); return SCAP_FAILURE; } fd = bpf_load_program(prog, program_type, insns_cnt, error, BPF_LOG_SIZE); if(fd < 0) { fprintf(stderr, "%s", error); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "bpf_load_program() err=%d event=%s message=%s", errno, event, error); free(error); return SCAP_FAILURE; } free(error); handle->m_bpf_prog_fds[handle->m_bpf_prog_cnt++] = fd; if(memcmp(event, "filler/", sizeof("filler/") - 1) == 0) { int prog_id; event += sizeof("filler/") - 1; if(*event == 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "filler name cannot be empty"); return SCAP_FAILURE; } prog_id = lookup_filler_id(event); if(prog_id == -1) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid filler name: %s", event); return SCAP_FAILURE; } err = bpf_map_update_elem(handle->m_bpf_map_fds[handle->m_bpf_prog_array_map_idx], &prog_id, &fd, BPF_ANY); if(err < 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "failure populating program array"); return SCAP_FAILURE; } handle->m_bpf_fillers[prog_id] = true; return SCAP_SUCCESS; } if(raw_tp) { efd = bpf_raw_tracepoint_open(event, fd); if(efd < 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "BPF_RAW_TRACEPOINT_OPEN: event %s: %s", event, scap_strerror(handle, errno)); return SCAP_FAILURE; } } else { strcpy(buf, "/sys/kernel/debug/tracing/events/"); strcat(buf, event); strcat(buf, "/id"); efd = open(buf, O_RDONLY, 0); if(efd < 0) { if(strcmp(event, "exceptions/page_fault_user") == 0 || strcmp(event, "exceptions/page_fault_kernel") == 0) { return SCAP_SUCCESS; } snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "failed to open event %s", event); return SCAP_FAILURE; } err = read(efd, buf, sizeof(buf)); if(err < 0 || err >= sizeof(buf)) { close(efd); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "read from '%s' failed '%s'", event, scap_strerror(handle, errno)); return SCAP_FAILURE; } close(efd); buf[err] = 0; id = atoi(buf); attr.config = id; efd = sys_perf_event_open(&attr, -1, 0, -1, 0); if(efd < 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "event %d fd %d err %s", id, efd, scap_strerror(handle, errno)); return SCAP_FAILURE; } if(ioctl(efd, PERF_EVENT_IOC_SET_BPF, fd)) { close(efd); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "PERF_EVENT_IOC_SET_BPF: %s", scap_strerror(handle, errno)); return SCAP_FAILURE; } } handle->m_bpf_event_fd[handle->m_bpf_prog_cnt - 1] = efd; return SCAP_SUCCESS; } static int32_t load_bpf_file(scap_t *handle, const char *path) { int j; int maps_shndx = 0; int strtabidx = 0; GElf_Shdr shdr; GElf_Shdr shdr_prog; Elf_Data *data; Elf_Data *data_prog; Elf_Data *symbols = NULL; char *shname; char *shname_prog; int nr_maps = 0; struct bpf_map_data maps[BPF_MAPS_MAX]; struct utsname osname; int32_t res = SCAP_FAILURE; if(uname(&osname)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can't call uname()"); return SCAP_FAILURE; } if(elf_version(EV_CURRENT) == EV_NONE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid ELF version"); return SCAP_FAILURE; } int program_fd = open(path, O_RDONLY, 0); if(program_fd < 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can't open BPF probe '%s': %s", path, scap_strerror(handle, errno)); return SCAP_FAILURE; } Elf *elf = elf_begin(program_fd, ELF_C_READ_MMAP_PRIVATE, NULL); if(!elf) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can't read ELF format"); goto cleanup; } GElf_Ehdr ehdr; if(gelf_getehdr(elf, &ehdr) != &ehdr) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can't read ELF header"); goto cleanup; } for(j = 0; j < ehdr.e_shnum; ++j) { if(get_elf_section(elf, j, &ehdr, &shname, &shdr, &data) != SCAP_SUCCESS) { continue; } if(strcmp(shname, "maps") == 0) { maps_shndx = j; } else if(shdr.sh_type == SHT_SYMTAB) { strtabidx = shdr.sh_link; symbols = data; } else if(strcmp(shname, "kernel_version") == 0) { if(strcmp(osname.release, data->d_buf)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "BPF probe is compiled for %s, but running version is %s", (char *) data->d_buf, osname.release); goto cleanup; } } else if(strcmp(shname, "probe_version") == 0) { if(strcmp(PROBE_VERSION, data->d_buf)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "BPF probe version is %s, but running version is %s", (char *) data->d_buf, PROBE_VERSION); goto cleanup; } } } if(!symbols) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "missing SHT_SYMTAB section"); goto cleanup; } if(maps_shndx) { if(load_elf_maps_section(handle, maps, maps_shndx, elf, symbols, strtabidx, &nr_maps) != SCAP_SUCCESS) { goto cleanup; } if(load_maps(handle, maps, nr_maps) != SCAP_SUCCESS) { goto cleanup; } } for(j = 0; j < ehdr.e_shnum; ++j) { if(get_elf_section(elf, j, &ehdr, &shname, &shdr, &data) != SCAP_SUCCESS) { continue; } if(shdr.sh_type == SHT_REL) { struct bpf_insn *insns; if(get_elf_section(elf, shdr.sh_info, &ehdr, &shname_prog, &shdr_prog, &data_prog) != SCAP_SUCCESS) { continue; } insns = (struct bpf_insn *) data_prog->d_buf; if(parse_relocations(handle, data, symbols, &shdr, insns, maps, nr_maps)) { continue; } } } for(j = 0; j < ehdr.e_shnum; ++j) { if(get_elf_section(elf, j, &ehdr, &shname, &shdr, &data) != SCAP_SUCCESS) { continue; } if(memcmp(shname, "tracepoint/", sizeof("tracepoint/") - 1) == 0 || memcmp(shname, "raw_tracepoint/", sizeof("raw_tracepoint/") - 1) == 0) { if(load_tracepoint(handle, shname, data->d_buf, data->d_size) != SCAP_SUCCESS) { goto cleanup; } } } res = SCAP_SUCCESS; cleanup: elf_end(elf); close(program_fd); return res; } static void *perf_event_mmap(scap_t *handle, int fd) { int page_size = getpagesize(); int ring_size = page_size * BUF_SIZE_PAGES; int header_size = page_size; int total_size = ring_size * 2 + header_size; // // All this playing with MAP_FIXED might be very very wrong, revisit // void *tmp = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); if(tmp == MAP_FAILED) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "mmap (1): %s", scap_strerror(handle, errno)); return MAP_FAILED; } // Map the second copy to allow us to handle the wrap case normally void *p1 = mmap(tmp + ring_size, ring_size + header_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); if(p1 == MAP_FAILED) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "mmap (2): %s", scap_strerror(handle, errno)); munmap(tmp, total_size); return MAP_FAILED; } ASSERT(p1 == tmp + ring_size); // Map the main copy void *p2 = mmap(tmp, ring_size + header_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); if(p2 == MAP_FAILED) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "mmap (3): %s", scap_strerror(handle, errno)); munmap(tmp, total_size); return MAP_FAILED; } ASSERT(p2 == tmp); return tmp; } static int32_t populate_syscall_routing_table_map(scap_t *handle) { int j; for(j = 0; j < SYSCALL_TABLE_SIZE; ++j) { long code = g_syscall_code_routing_table[j]; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SYSCALL_CODE_ROUTING_TABLE], &j, &code, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SYSCALL_CODE_ROUTING_TABLE bpf_map_update_elem < 0"); return SCAP_FAILURE; } } return SCAP_SUCCESS; } static int32_t populate_syscall_table_map(scap_t *handle) { int j; for(j = 0; j < SYSCALL_TABLE_SIZE; ++j) { const struct syscall_evt_pair *p = &g_syscall_table[j]; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SYSCALL_TABLE], &j, p, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SYSCALL_TABLE bpf_map_update_elem < 0"); return SCAP_FAILURE; } } return SCAP_SUCCESS; } static int32_t populate_event_table_map(scap_t *handle) { int j; for(j = 0; j < PPM_EVENT_MAX; ++j) { const struct ppm_event_info *e = &g_event_info[j]; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_EVENT_INFO_TABLE], &j, e, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_EVENT_INFO_TABLE bpf_map_update_elem < 0"); return SCAP_FAILURE; } } return SCAP_SUCCESS; } static int32_t populate_fillers_table_map(scap_t *handle) { int j; for(j = 0; j < PPM_EVENT_MAX; ++j) { const struct ppm_event_entry *e = &g_ppm_events[j]; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_FILLERS_TABLE], &j, e, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_FILLERS_TABLE bpf_map_update_elem < 0"); return SCAP_FAILURE; } } for(j = 0; j < PPM_FILLER_MAX; ++j) { if(!handle->m_bpf_fillers[j]) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Missing filler %d (%s)\n", j, g_filler_names[j]); return SCAP_FAILURE; } } return SCAP_SUCCESS; } // // This is needed to make sure that the driver can properly // lookup sockets. We generate a fake socket system call // at the beginning so the calibration will surely take place. // For more info, read the corresponding filler in kernel space. // static int32_t calibrate_socket_file_ops() { int fd = socket(AF_INET, SOCK_DGRAM, 0); if(fd == -1) { return SCAP_FAILURE; } close(fd); return SCAP_SUCCESS; } int32_t scap_bpf_start_capture(scap_t *handle) { struct sysdig_bpf_settings settings; int k = 0; if(bpf_map_lookup_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_lookup_elem < 0"); return SCAP_FAILURE; } settings.capture_enabled = true; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_update_elem < 0"); return SCAP_FAILURE; } if(calibrate_socket_file_ops() != SCAP_SUCCESS) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "calibrate_socket_file_ops"); return SCAP_FAILURE; } return SCAP_SUCCESS; } int32_t scap_bpf_stop_capture(scap_t *handle) { struct sysdig_bpf_settings settings; int k = 0; if(bpf_map_lookup_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_lookup_elem < 0"); return SCAP_FAILURE; } settings.capture_enabled = false; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_update_elem < 0"); return SCAP_FAILURE; } return SCAP_SUCCESS; } int32_t scap_bpf_set_snaplen(scap_t* handle, uint32_t snaplen) { struct sysdig_bpf_settings settings; int k = 0; if(snaplen > RW_MAX_SNAPLEN) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "snaplen can't exceed %d\n", RW_MAX_SNAPLEN); return SCAP_FAILURE; } if(bpf_map_lookup_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_lookup_elem < 0"); return SCAP_FAILURE; } settings.snaplen = snaplen; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_update_elem < 0"); return SCAP_FAILURE; } return SCAP_SUCCESS; } int32_t scap_bpf_set_fullcapture_port_range(scap_t* handle, uint16_t range_start, uint16_t range_end) { struct sysdig_bpf_settings settings; int k = 0; if(bpf_map_lookup_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_lookup_elem < 0"); return SCAP_FAILURE; } settings.fullcapture_port_range_start = range_start; settings.fullcapture_port_range_end = range_end; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_update_elem < 0"); return SCAP_FAILURE; } return SCAP_SUCCESS; } int32_t scap_bpf_set_statsd_port(scap_t* const handle, const uint16_t port) { struct sysdig_bpf_settings settings = {}; int k = 0; if(bpf_map_lookup_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_lookup_elem < 0"); return SCAP_FAILURE; } settings.statsd_port = port; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_update_elem < 0"); return SCAP_FAILURE; } return SCAP_SUCCESS; } int32_t scap_bpf_disable_dynamic_snaplen(scap_t* handle) { struct sysdig_bpf_settings settings; int k = 0; if(bpf_map_lookup_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_lookup_elem < 0"); return SCAP_FAILURE; } settings.do_dynamic_snaplen = false; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_update_elem < 0"); return SCAP_FAILURE; } return SCAP_SUCCESS; } int32_t scap_bpf_start_dropping_mode(scap_t* handle, uint32_t sampling_ratio) { switch(sampling_ratio) { case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: break; default: snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid sampling ratio size"); return SCAP_FAILURE; } struct sysdig_bpf_settings settings; int k = 0; if(bpf_map_lookup_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_lookup_elem < 0"); return SCAP_FAILURE; } settings.sampling_ratio = sampling_ratio; settings.dropping_mode = true; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_update_elem < 0"); return SCAP_FAILURE; } return SCAP_SUCCESS; } int32_t scap_bpf_stop_dropping_mode(scap_t* handle) { struct sysdig_bpf_settings settings; int k = 0; if(bpf_map_lookup_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_lookup_elem < 0"); return SCAP_FAILURE; } settings.sampling_ratio = 1; settings.dropping_mode = false; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_update_elem < 0"); return SCAP_FAILURE; } return SCAP_SUCCESS; } int32_t scap_bpf_enable_dynamic_snaplen(scap_t* handle) { struct sysdig_bpf_settings settings; int k = 0; if(bpf_map_lookup_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_lookup_elem < 0"); return SCAP_FAILURE; } settings.do_dynamic_snaplen = true; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_update_elem < 0"); return SCAP_FAILURE; } return SCAP_SUCCESS; } int32_t scap_bpf_enable_page_faults(scap_t* handle) { struct sysdig_bpf_settings settings; int k = 0; if(bpf_map_lookup_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_lookup_elem < 0"); return SCAP_FAILURE; } settings.page_faults = true; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_update_elem < 0"); return SCAP_FAILURE; } return SCAP_SUCCESS; } int32_t scap_bpf_enable_tracers_capture(scap_t* handle) { struct sysdig_bpf_settings settings; int k = 0; if(bpf_map_lookup_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_lookup_elem < 0"); return SCAP_FAILURE; } settings.tracers_enabled = true; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_update_elem < 0"); return SCAP_FAILURE; } return SCAP_SUCCESS; } int32_t scap_bpf_close(scap_t *handle) { int j; int page_size = getpagesize(); int ring_size = page_size * BUF_SIZE_PAGES; int header_size = page_size; int total_size = ring_size * 2 + header_size; for(j = 0; j < handle->m_ndevs; j++) { if(handle->m_devs[j].m_buffer != MAP_FAILED) { #ifdef _DEBUG int ret; ret = munmap(handle->m_devs[j].m_buffer, total_size); #else munmap(handle->m_devs[j].m_buffer, total_size); #endif ASSERT(ret == 0); } if(handle->m_devs[j].m_fd > 0) { close(handle->m_devs[j].m_fd); } } for(j = 0; j < sizeof(handle->m_bpf_event_fd) / sizeof(handle->m_bpf_event_fd[0]); ++j) { if(handle->m_bpf_event_fd[j] > 0) { close(handle->m_bpf_event_fd[j]); handle->m_bpf_event_fd[j] = 0; } } for(j = 0; j < sizeof(handle->m_bpf_prog_fds) / sizeof(handle->m_bpf_prog_fds[0]); ++j) { if(handle->m_bpf_prog_fds[j] > 0) { close(handle->m_bpf_prog_fds[j]); handle->m_bpf_prog_fds[j] = 0; } } for(j = 0; j < sizeof(handle->m_bpf_map_fds) / sizeof(handle->m_bpf_map_fds[0]); ++j) { if(handle->m_bpf_map_fds[j] > 0) { close(handle->m_bpf_map_fds[j]); handle->m_bpf_map_fds[j] = 0; } } handle->m_bpf_prog_cnt = 0; handle->m_bpf_prog_array_map_idx = -1; return SCAP_SUCCESS; } // // This is completely horrible, revisit this shameful code // with a proper solution // static int32_t set_boot_time(scap_t *handle, uint64_t *boot_time) { struct timespec ts_uptime; struct timeval tv_now; uint64_t now; uint64_t uptime; if(gettimeofday(&tv_now, NULL)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "gettimeofday"); return SCAP_FAILURE; } now = tv_now.tv_sec * (uint64_t) 1000000000 + tv_now.tv_usec * 1000; if(clock_gettime(CLOCK_BOOTTIME, &ts_uptime)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "clock_gettime"); return SCAP_FAILURE; } uptime = ts_uptime.tv_sec * (uint64_t) 1000000000 + ts_uptime.tv_nsec; *boot_time = now - uptime; return SCAP_SUCCESS; } static int32_t set_runtime_params(scap_t *handle) { struct rlimit rl; rl.rlim_max = RLIM_INFINITY; rl.rlim_cur = rl.rlim_max; if(setrlimit(RLIMIT_MEMLOCK, &rl)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "setrlimit failed"); return SCAP_FAILURE; } FILE *f = fopen("/proc/sys/net/core/bpf_jit_enable", "w"); if(!f) { // snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Can't open /proc/sys/net/core/bpf_jit_enable"); // return SCAP_FAILURE; // Not every kernel has BPF_JIT enabled. Fix this after COS changes. return SCAP_SUCCESS; } if(fprintf(f, "1") != 1) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Can't write to /proc/sys/net/core/bpf_jit_enable"); fclose(f); return SCAP_FAILURE; } fclose(f); f = fopen("/proc/sys/net/core/bpf_jit_harden", "w"); if(!f) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Can't open /proc/sys/net/core/bpf_jit_harden"); return SCAP_FAILURE; } if(fprintf(f, "0") != 1) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Can't write to /proc/sys/net/core/bpf_jit_harden"); fclose(f); return SCAP_FAILURE; } fclose(f); f = fopen("/proc/sys/net/core/bpf_jit_kallsyms", "w"); if(!f) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Can't open /proc/sys/net/core/bpf_jit_kallsyms"); return SCAP_FAILURE; } if(fprintf(f, "1") != 1) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Can't write to /proc/sys/net/core/bpf_jit_kallsyms"); fclose(f); return SCAP_FAILURE; } fclose(f); return SCAP_SUCCESS; } static int32_t set_default_settings(scap_t *handle) { struct sysdig_bpf_settings settings; if(set_boot_time(handle, &settings.boot_time) != SCAP_SUCCESS) { return SCAP_FAILURE; } settings.socket_file_ops = NULL; settings.snaplen = RW_SNAPLEN; settings.sampling_ratio = 1; settings.capture_enabled = false; settings.do_dynamic_snaplen = false; settings.page_faults = false; settings.dropping_mode = false; settings.is_dropping = false; settings.tracers_enabled = false; settings.fullcapture_port_range_start = 0; settings.fullcapture_port_range_end = 0; settings.statsd_port = 8125; int k = 0; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_SETTINGS_MAP], &k, &settings, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_SETTINGS_MAP bpf_map_update_elem < 0"); return SCAP_FAILURE; } return SCAP_SUCCESS; } int32_t scap_bpf_load(scap_t *handle, const char *bpf_probe) { int online_cpu; int j; if(set_runtime_params(handle) != SCAP_SUCCESS) { return SCAP_FAILURE; } handle->m_bpf_prog_array_map_idx = -1; if(!bpf_probe) { ASSERT(false); return SCAP_FAILURE; } if(load_bpf_file(handle, bpf_probe) != SCAP_SUCCESS) { return SCAP_FAILURE; } if(populate_syscall_routing_table_map(handle) != SCAP_SUCCESS) { return SCAP_FAILURE; } if(populate_syscall_table_map(handle) != SCAP_SUCCESS) { return SCAP_FAILURE; } if(populate_event_table_map(handle) != SCAP_SUCCESS) { return SCAP_FAILURE; } if(populate_fillers_table_map(handle) != SCAP_SUCCESS) { return SCAP_FAILURE; } // // Open and initialize all the devices // online_cpu = 0; for(j = 0; j < handle->m_ncpus; ++j) { struct perf_event_attr attr = { .sample_type = PERF_SAMPLE_RAW, .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_BPF_OUTPUT, }; int pmu_fd; if(j > 0) { char filename[SCAP_MAX_PATH_SIZE]; int online; FILE *fp; snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%d/online", j); fp = fopen(filename, "r"); if(fp == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can't open %s: %s", filename, scap_strerror(handle, errno)); return SCAP_FAILURE; } if(fscanf(fp, "%d", &online) != 1) { fclose(fp); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can't read %s: %s", filename, scap_strerror(handle, errno)); return SCAP_FAILURE; } fclose(fp); if(!online) { continue; } } if(online_cpu >= handle->m_ndevs) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "processors online: %d, expected: %d", online_cpu, handle->m_ndevs); return SCAP_FAILURE; } pmu_fd = sys_perf_event_open(&attr, -1, j, -1, 0); if(pmu_fd < 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "pmu_fd < 0: %s", scap_strerror(handle, errno)); return SCAP_FAILURE; } handle->m_devs[online_cpu].m_fd = pmu_fd; if(bpf_map_update_elem(handle->m_bpf_map_fds[SYSDIG_PERF_MAP], &j, &pmu_fd, BPF_ANY) != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "SYSDIG_PERF_MAP bpf_map_update_elem < 0: %s", scap_strerror(handle, errno)); return SCAP_FAILURE; } if(ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "PERF_EVENT_IOC_ENABLE"); return SCAP_FAILURE; } // // Map the ring buffer // handle->m_devs[online_cpu].m_buffer = perf_event_mmap(handle, pmu_fd); if(handle->m_devs[online_cpu].m_buffer == MAP_FAILED) { return SCAP_FAILURE; } ++online_cpu; } if(online_cpu != handle->m_ndevs) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "processors online: %d, expected: %d", j, handle->m_ndevs); return SCAP_FAILURE; } if(set_default_settings(handle) != SCAP_SUCCESS) { return SCAP_FAILURE; } return SCAP_SUCCESS; } struct ppm_proclist_info *scap_bpf_get_threadlist(scap_t *handle) { DIR *dir_p = NULL; DIR *taskdir_p = NULL; FILE *fp = NULL; struct ppm_proclist_info *res = NULL; struct dirent *dir_entry_p; char procdirname[SCAP_MAX_PATH_SIZE]; handle->m_driver_procinfo->n_entries = 0; snprintf(procdirname, sizeof(procdirname), "%s/proc", scap_get_host_root()); dir_p = opendir(procdirname); if(dir_p == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error opening the %s directory", procdirname); goto error; } while((dir_entry_p = readdir(dir_p)) != NULL) { char tasksdirname[SCAP_MAX_PATH_SIZE]; struct dirent *taskdir_entry_p; DIR *taskdir_p; if(strspn(dir_entry_p->d_name, "0123456789") != strlen(dir_entry_p->d_name)) { continue; } snprintf(tasksdirname, sizeof(tasksdirname), "%s/%s/task", procdirname, dir_entry_p->d_name); taskdir_p = opendir(tasksdirname); if(taskdir_p == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error opening the %s directory", tasksdirname); continue; } while((taskdir_entry_p = readdir(taskdir_p)) != NULL) { char filename[SCAP_MAX_PATH_SIZE]; unsigned long utime; unsigned long stime; int tid; if(strspn(taskdir_entry_p->d_name, "0123456789") != strlen(taskdir_entry_p->d_name)) { continue; } snprintf(filename, sizeof(filename), "%s/%s/stat", tasksdirname, taskdir_entry_p->d_name); fp = fopen(filename, "r"); if(fp == NULL) { continue; } if(fscanf(fp, "%d %*[^)] %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lu %lu", &tid, &utime, &stime) != 3) { fclose(fp); fp = NULL; continue; } if(handle->m_driver_procinfo->n_entries == handle->m_driver_procinfo->max_entries) { if(!scap_alloc_proclist_info(handle, handle->m_driver_procinfo->n_entries + 256)) { goto error; } } handle->m_driver_procinfo->entries[handle->m_driver_procinfo->n_entries].pid = tid; handle->m_driver_procinfo->entries[handle->m_driver_procinfo->n_entries].utime = utime; handle->m_driver_procinfo->entries[handle->m_driver_procinfo->n_entries].stime = stime; ++handle->m_driver_procinfo->n_entries; fclose(fp); fp = NULL; } closedir(taskdir_p); taskdir_p = NULL; } res = handle->m_driver_procinfo; error: if(dir_p) { closedir(dir_p); } if(taskdir_p) { closedir(taskdir_p); } if(fp) { fclose(fp); } return res; } int32_t scap_bpf_get_stats(scap_t* handle, OUT scap_stats* stats) { int j; for(j = 0; j < handle->m_ncpus; j++) { struct sysdig_bpf_per_cpu_state v; if(bpf_map_lookup_elem(handle->m_bpf_map_fds[SYSDIG_LOCAL_STATE_MAP], &j, &v)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Error looking up local state %d\n", j); return SCAP_FAILURE; } stats->n_evts += v.n_evts; stats->n_drops_buffer += handle->m_devs[j].m_evt_lost + v.n_drops_buffer; stats->n_drops_pf += v.n_drops_pf; stats->n_drops_bug += v.n_drops_bug; stats->n_drops += handle->m_devs[j].m_evt_lost + v.n_drops_buffer + v.n_drops_pf + v.n_drops_bug; } return SCAP_SUCCESS; } int32_t scap_bpf_get_n_tracepoint_hit(scap_t* handle, long* ret) { int j; for(j = 0; j < handle->m_ncpus; j++) { struct sysdig_bpf_per_cpu_state v; if(bpf_map_lookup_elem(handle->m_bpf_map_fds[SYSDIG_LOCAL_STATE_MAP], &j, &v)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Error looking up local state %d\n", j); return SCAP_FAILURE; } ret[j] = v.n_evts; } return SCAP_SUCCESS; } sysdig-0.26.4/userspace/libscap/scap_bpf.h000066400000000000000000000121241352731327100204460ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #ifndef _SCAP_BPF_H #define _SCAP_BPF_H #include "compat/perf_event.h" struct perf_event_sample { struct perf_event_header header; uint32_t size; char data[]; }; struct perf_lost_sample { struct perf_event_header header; uint64_t id; uint64_t lost; }; int32_t scap_bpf_load(scap_t *handle, const char *bpf_probe); int32_t scap_bpf_start_capture(scap_t *handle); int32_t scap_bpf_stop_capture(scap_t *handle); int32_t scap_bpf_close(scap_t *handle); int32_t scap_bpf_set_snaplen(scap_t* handle, uint32_t snaplen); int32_t scap_bpf_set_fullcapture_port_range(scap_t* handle, uint16_t range_start, uint16_t range_end); int32_t scap_bpf_set_statsd_port(scap_t* handle, uint16_t port); int32_t scap_bpf_enable_dynamic_snaplen(scap_t* handle); int32_t scap_bpf_disable_dynamic_snaplen(scap_t* handle); int32_t scap_bpf_enable_page_faults(scap_t* handle); int32_t scap_bpf_start_dropping_mode(scap_t* handle, uint32_t sampling_ratio); int32_t scap_bpf_stop_dropping_mode(scap_t* handle); int32_t scap_bpf_enable_tracers_capture(scap_t* handle); struct ppm_proclist_info *scap_bpf_get_threadlist(scap_t *handle); int32_t scap_bpf_get_stats(scap_t* handle, OUT scap_stats* stats); int32_t scap_bpf_get_n_tracepoint_hit(scap_t* handle, long* ret); static inline scap_evt *scap_bpf_evt_from_perf_sample(void *evt) { struct perf_event_sample *perf_evt = (struct perf_event_sample *) evt; ASSERT(perf_evt->header.type == PERF_RECORD_SAMPLE); return (scap_evt *) perf_evt->data; } static inline void scap_bpf_get_buf_pointers(char *buf, uint64_t *phead, uint64_t *ptail, uint64_t *pread_size) { struct perf_event_mmap_page *header; uint64_t begin; uint64_t end; header = (struct perf_event_mmap_page *) buf; *phead = header->data_head; *ptail = header->data_tail; // clang-format off asm volatile("" ::: "memory"); // clang-format on begin = *ptail % header->data_size; end = *phead % header->data_size; if(begin > end) { *pread_size = header->data_size - begin + end; } else { *pread_size = end - begin; } } static inline int32_t scap_bpf_advance_to_evt(scap_t *handle, uint16_t cpuid, bool skip_current, char *cur_evt, char **next_evt, uint32_t *len) { struct scap_device *dev; void *base; void *begin; dev = &handle->m_devs[cpuid]; struct perf_event_mmap_page *header = (struct perf_event_mmap_page *) dev->m_buffer; base = ((char *) header) + header->data_offset; begin = cur_evt; while(*len) { struct perf_event_header *e = begin; ASSERT(*len >= sizeof(*e)); ASSERT(*len >= e->size); if(e->type == PERF_RECORD_SAMPLE) { #ifdef _DEBUG struct perf_event_sample *sample = (struct perf_event_sample *) e; #endif ASSERT(*len >= sizeof(*sample)); ASSERT(*len >= sample->size); ASSERT(e->size == sizeof(*e) + sizeof(sample->size) + sample->size); ASSERT(((scap_evt *) sample->data)->len <= sample->size); if(skip_current) { skip_current = false; } else { *next_evt = (char *) e; break; } } else if(e->type == PERF_RECORD_LOST) { struct perf_lost_sample *lost = (struct perf_lost_sample *) e; ASSERT(*len >= sizeof(*lost)); dev->m_evt_lost += lost->lost; } else { printf("Unknown event type=%d size=%d\n", e->type, e->size); ASSERT(false); } if(begin + e->size > base + header->data_size) { begin = begin + e->size - header->data_size; } else if(begin + e->size == base + header->data_size) { begin = base; } else { begin += e->size; } *len -= e->size; } return SCAP_SUCCESS; } static inline void scap_bpf_advance_tail(scap_t *handle, uint32_t cpuid) { struct perf_event_mmap_page *header; struct scap_device *dev; dev = &handle->m_devs[cpuid]; header = (struct perf_event_mmap_page *)dev->m_buffer; // clang-format off asm volatile("" ::: "memory"); // clang-format on ASSERT(dev->m_lastreadsize > 0); header->data_tail += dev->m_lastreadsize; dev->m_lastreadsize = 0; } static inline int32_t scap_bpf_readbuf(scap_t *handle, uint32_t cpuid, char **buf, uint32_t *len) { struct perf_event_mmap_page *header; struct scap_device *dev; uint64_t tail; uint64_t head; uint64_t read_size; char *p; dev = &handle->m_devs[cpuid]; header = (struct perf_event_mmap_page *) dev->m_buffer; ASSERT(dev->m_lastreadsize == 0); scap_bpf_get_buf_pointers((char *) header, &head, &tail, &read_size); dev->m_lastreadsize = read_size; p = ((char *) header) + header->data_offset + tail % header->data_size; *len = read_size; return scap_bpf_advance_to_evt(handle, cpuid, false, p, buf, len); } #endif sysdig-0.26.4/userspace/libscap/scap_event.c000066400000000000000000000030421352731327100210120ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #ifdef _WIN32 #include #else #include #include #endif // _WIN32 #include "scap.h" #include "scap-int.h" // // Get the event info table // const struct ppm_event_info* scap_get_event_info_table() { ASSERT(validate_info_table_size()); return g_event_info; } // // Get the syscall info table // const struct ppm_syscall_desc* scap_get_syscall_info_table() { return g_syscall_info_table; } uint32_t scap_event_getlen(scap_evt* e) { return e->len; } uint64_t scap_event_get_num(scap_t* handle) { return handle->m_evtcnt; } void scap_event_reset_count(scap_t* handle) { handle->m_evtcnt = 0; } uint64_t scap_event_get_ts(scap_evt* e) { return e->ts; } #ifdef PPM_ENABLE_SENTINEL uint32_t scap_event_get_sentinel_begin(scap_evt* e) { return e->sentinel_begin; } #endif const struct ppm_event_info* scap_event_getinfo(scap_evt* e) { return &(g_event_info[e->type]); } sysdig-0.26.4/userspace/libscap/scap_fds.c000066400000000000000000001444261352731327100204610ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include "scap.h" #include "scap-int.h" #include "scap_savefile.h" #include #include #include #include "uthash.h" #ifdef _WIN32 #include #elif defined(__APPLE__) #include #include #include #include #include #else #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include #include #if defined(__linux__) #if HAVE_SYS_MKDEV_H #include #endif #ifdef HAVE_SYS_SYSMACROS_H #include #endif #include #include //#include //#include #endif #endif #define SOCKET_SCAN_BUFFER_SIZE 1024 * 1024 int32_t scap_fd_print_ipv6_socket_info(scap_t *handle, scap_fdinfo *fdi, OUT char *str, uint32_t stlen) { char source_address[100]; char destination_address[100]; if(NULL == inet_ntop(AF_INET6,fdi->info.ipv6info.sip,source_address,100)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not convert IPv6 source address 0x%x%x%x%x (%s)", fdi->info.ipv6info.sip[0], fdi->info.ipv6info.sip[1], fdi->info.ipv6info.sip[2], fdi->info.ipv6info.sip[3], scap_strerror(handle, errno)); return SCAP_FAILURE; } if(NULL == inet_ntop(AF_INET6,fdi->info.ipv6info.dip,destination_address,100)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not convert IPv6 source address 0x%x%x%x%x (%s)", fdi->info.ipv6info.dip[0], fdi->info.ipv6info.dip[1], fdi->info.ipv6info.dip[2], fdi->info.ipv6info.dip[3], scap_strerror(handle, errno)); return SCAP_FAILURE; } snprintf(str,stlen,"%s:%u->%s:%u",source_address,fdi->info.ipv6info.sport,destination_address,fdi->info.ipv6info.dport); return SCAP_SUCCESS; } int32_t scap_fd_print_ipv6_server_socket_info(scap_t *handle, scap_fdinfo *fdi, OUT char *str, uint32_t stlen) { char address[100]; if(NULL == inet_ntop(AF_INET6,fdi->info.ipv6serverinfo.ip,address,100)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not convert IPv6 source address 0x%x%x%x%x (%s)", fdi->info.ipv6serverinfo.ip[0], fdi->info.ipv6serverinfo.ip[1], fdi->info.ipv6serverinfo.ip[2], fdi->info.ipv6serverinfo.ip[3], scap_strerror(handle, errno)); return SCAP_FAILURE; } snprintf(str,stlen,"%s:%u->:::*",address,fdi->info.ipv6serverinfo.port); return SCAP_SUCCESS; } // // Convert an fd entry's info into a string // int32_t scap_fd_info_to_string(scap_t *handle, scap_fdinfo *fdi, OUT char *str, uint32_t stlen) { // // Input validation // if((fdi)->type == SCAP_FD_UNKNOWN) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "fd type unknown"); return SCAP_FAILURE; } switch(fdi->type) { case SCAP_FD_IPV4_SOCK: snprintf(str, stlen, "%u.%u.%u.%u:%u->%u.%u.%u.%u:%u", fdi->info.ipv4info.sip >> 24, fdi->info.ipv4info.sip >> 16 & 0xff, fdi->info.ipv4info.sip >> 8 & 0xff, fdi->info.ipv4info.sip & 0xff, (uint32_t)fdi->info.ipv4info.sport, fdi->info.ipv4info.dip >> 24, fdi->info.ipv4info.dip >> 16 & 0xff, fdi->info.ipv4info.dip >> 8 & 0xff, fdi->info.ipv4info.dip & 0xff, (uint32_t)fdi->info.ipv4info.dport); break; case SCAP_FD_IPV4_SERVSOCK: snprintf(str, stlen, "%u.%u.%u.%u:%u", fdi->info.ipv4serverinfo.ip >> 24, fdi->info.ipv4serverinfo.ip >> 16 & 0xff, fdi->info.ipv4serverinfo.ip >> 8 & 0xff, fdi->info.ipv4serverinfo.ip & 0xff, (uint32_t)fdi->info.ipv4serverinfo.port); break; case SCAP_FD_IPV6_SOCK: return scap_fd_print_ipv6_socket_info(handle,fdi,str,stlen); break; case SCAP_FD_IPV6_SERVSOCK: return scap_fd_print_ipv6_server_socket_info(handle,fdi,str,stlen); break; case SCAP_FD_FIFO: snprintf(str, stlen, ""); break; case SCAP_FD_SIGNALFD: snprintf(str, stlen, ""); break; case SCAP_FD_EVENTPOLL: snprintf(str, stlen, ""); break; case SCAP_FD_TIMERFD: snprintf(str, stlen, ""); break; case SCAP_FD_EVENT: snprintf(str, stlen, ""); break; case SCAP_FD_INOTIFY: snprintf(str, stlen, ""); break; case SCAP_FD_UNIX_SOCK: snprintf(str, stlen, "%"PRIi64" %"PRIu64" %"PRIX64"-> %"PRIX64" %s", fdi->fd,fdi->ino, fdi->info.unix_socket_info.source,fdi->info.unix_socket_info.destination, fdi->info.unix_socket_info.fname); break; case SCAP_FD_FILE_V2: case SCAP_FD_FILE: case SCAP_FD_DIRECTORY: break; case SCAP_FD_UNSUPPORTED: snprintf(str, stlen, ""); break; case SCAP_FD_NETLINK: snprintf(str, stlen, ""); break; default: ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "fd type unrecognized"); return SCAP_FAILURE; } return SCAP_SUCCESS; } // // Calculate the length on disk of an fd entry's info // uint32_t scap_fd_info_len(scap_fdinfo *fdi) { // // NB: new fields must be appended // uint32_t res = sizeof(uint32_t) + sizeof(fdi->ino) + 1 + sizeof(fdi->fd); switch(fdi->type) { case SCAP_FD_IPV4_SOCK: res += 4 + // sip 4 + // dip 2 + // sport 2 + // dport 1; // l4proto break; case SCAP_FD_IPV4_SERVSOCK: res += 4 + // ip 2 + // port 1; // l4proto break; case SCAP_FD_IPV6_SOCK: res += sizeof(uint32_t) * 4 + // sip sizeof(uint32_t) * 4 + // dip sizeof(uint16_t) + // sport sizeof(uint16_t) + // dport sizeof(uint8_t); // l4proto break; case SCAP_FD_IPV6_SERVSOCK: res += sizeof(uint32_t) * 4 + // ip sizeof(uint16_t) + // port sizeof(uint8_t); // l4proto break; case SCAP_FD_UNIX_SOCK: res += sizeof(uint64_t) + // unix source sizeof(uint64_t) + // unix destination (uint32_t)strnlen(fdi->info.unix_socket_info.fname, SCAP_MAX_PATH_SIZE) + 2; break; case SCAP_FD_FILE_V2: res += sizeof(uint32_t) + // open_flags (uint32_t)strnlen(fdi->info.regularinfo.fname, SCAP_MAX_PATH_SIZE) + 2 + sizeof(uint32_t); // dev break; case SCAP_FD_FIFO: case SCAP_FD_FILE: case SCAP_FD_DIRECTORY: case SCAP_FD_UNSUPPORTED: case SCAP_FD_EVENT: case SCAP_FD_SIGNALFD: case SCAP_FD_EVENTPOLL: case SCAP_FD_INOTIFY: case SCAP_FD_TIMERFD: case SCAP_FD_NETLINK: res += (uint32_t)strnlen(fdi->info.fname, SCAP_MAX_PATH_SIZE) + 2; // 2 is the length field before the string break; default: ASSERT(false); break; } return res; } int scap_dump_write(scap_dumper_t *d, void* buf, unsigned len); // // Write the given fd info to disk // int32_t scap_fd_write_to_disk(scap_t *handle, scap_fdinfo *fdi, scap_dumper_t *d, uint32_t len) { uint8_t type = (uint8_t)fdi->type; uint16_t stlen; if(scap_dump_write(d, &(len), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(fdi->fd), sizeof(uint64_t)) != sizeof(uint64_t) || scap_dump_write(d, &(fdi->ino), sizeof(uint64_t)) != sizeof(uint64_t) || scap_dump_write(d, &(type), sizeof(uint8_t)) != sizeof(uint8_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi1)"); return SCAP_FAILURE; } switch(fdi->type) { case SCAP_FD_IPV4_SOCK: if(scap_dump_write(d, &(fdi->info.ipv4info.sip), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(fdi->info.ipv4info.dip), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(fdi->info.ipv4info.sport), sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, &(fdi->info.ipv4info.dport), sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, &(fdi->info.ipv4info.l4proto), sizeof(uint8_t)) != sizeof(uint8_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi2)"); return SCAP_FAILURE; } break; case SCAP_FD_IPV4_SERVSOCK: if(scap_dump_write(d, &(fdi->info.ipv4serverinfo.ip), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(fdi->info.ipv4serverinfo.port), sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, &(fdi->info.ipv4serverinfo.l4proto), sizeof(uint8_t)) != sizeof(uint8_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi3)"); return SCAP_FAILURE; } break; case SCAP_FD_IPV6_SOCK: if(scap_dump_write(d, (char*)fdi->info.ipv6info.sip, sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4 || scap_dump_write(d, (char*)fdi->info.ipv6info.dip, sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4 || scap_dump_write(d, &(fdi->info.ipv6info.sport), sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, &(fdi->info.ipv6info.dport), sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, &(fdi->info.ipv6info.l4proto), sizeof(uint8_t)) != sizeof(uint8_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi7)"); } break; case SCAP_FD_IPV6_SERVSOCK: if(scap_dump_write(d, &(fdi->info.ipv6serverinfo.ip), sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4 || scap_dump_write(d, &(fdi->info.ipv6serverinfo.port), sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, &(fdi->info.ipv6serverinfo.l4proto), sizeof(uint8_t)) != sizeof(uint8_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi8)"); } break; case SCAP_FD_UNIX_SOCK: if(scap_dump_write(d, &(fdi->info.unix_socket_info.source), sizeof(uint64_t)) != sizeof(uint64_t) || scap_dump_write(d, &(fdi->info.unix_socket_info.destination), sizeof(uint64_t)) != sizeof(uint64_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi4)"); return SCAP_FAILURE; } stlen = (uint16_t)strnlen(fdi->info.unix_socket_info.fname, SCAP_MAX_PATH_SIZE); if(scap_dump_write(d, &stlen, sizeof(uint16_t)) != sizeof(uint16_t) || (stlen > 0 && scap_dump_write(d, fdi->info.unix_socket_info.fname, stlen) != stlen)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi5)"); return SCAP_FAILURE; } break; case SCAP_FD_FILE_V2: if(scap_dump_write(d, &(fdi->info.regularinfo.open_flags), sizeof(uint32_t)) != sizeof(uint32_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi1)"); return SCAP_FAILURE; } stlen = (uint16_t)strnlen(fdi->info.regularinfo.fname, SCAP_MAX_PATH_SIZE); if(scap_dump_write(d, &stlen, sizeof(uint16_t)) != sizeof(uint16_t) || (stlen > 0 && scap_dump_write(d, fdi->info.regularinfo.fname, stlen) != stlen)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi1)"); return SCAP_FAILURE; } if(scap_dump_write(d, &(fdi->info.regularinfo.dev), sizeof(uint32_t)) != sizeof(uint32_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (dev)"); return SCAP_FAILURE; } break; case SCAP_FD_FIFO: case SCAP_FD_FILE: case SCAP_FD_DIRECTORY: case SCAP_FD_UNSUPPORTED: case SCAP_FD_EVENT: case SCAP_FD_SIGNALFD: case SCAP_FD_EVENTPOLL: case SCAP_FD_INOTIFY: case SCAP_FD_TIMERFD: case SCAP_FD_NETLINK: stlen = (uint16_t)strnlen(fdi->info.fname, SCAP_MAX_PATH_SIZE); if(scap_dump_write(d, &stlen, sizeof(uint16_t)) != sizeof(uint16_t) || (stlen > 0 && scap_dump_write(d, fdi->info.fname, stlen) != stlen)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi6)"); return SCAP_FAILURE; } break; case SCAP_FD_UNKNOWN: // Ignore UNKNOWN fds without failing ASSERT(false); break; default: snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Unknown fdi type %d", fdi->type); ASSERT(false); return SCAP_FAILURE; } return SCAP_SUCCESS; } uint32_t scap_fd_read_prop_from_disk(scap_t *handle, OUT void *target, size_t expected_size, OUT size_t *nbytes, gzFile f) { size_t readsize; readsize = gzread(f, target, (unsigned int)expected_size); CHECK_READ_SIZE(readsize, expected_size); (*nbytes) += readsize; return SCAP_SUCCESS; } uint32_t scap_fd_read_fname_from_disk(scap_t* handle, char* fname,OUT size_t* nbytes, gzFile f) { size_t readsize; uint16_t stlen; readsize = gzread(f, &(stlen), sizeof(uint16_t)); CHECK_READ_SIZE(readsize, sizeof(uint16_t)); if(stlen >= SCAP_MAX_PATH_SIZE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid filename len %"PRId32, stlen); return SCAP_FAILURE; } (*nbytes) += readsize; readsize = gzread(f, fname, stlen); CHECK_READ_SIZE(readsize, stlen); (*nbytes) += stlen; // NULL-terminate the string fname[stlen] = 0; return SCAP_SUCCESS; } // // Populate the given fd by reading the info from disk // Returns the number of read bytes. // uint32_t scap_fd_read_from_disk(scap_t *handle, OUT scap_fdinfo *fdi, OUT size_t *nbytes, uint32_t block_type, gzFile f) { uint8_t type; uint32_t toread; int fseekres; uint32_t sub_len = 0; uint32_t res = SCAP_SUCCESS; *nbytes = 0; if((block_type == FDL_BLOCK_TYPE_V2 && scap_fd_read_prop_from_disk(handle, &sub_len, sizeof(uint32_t), nbytes, f)) || scap_fd_read_prop_from_disk(handle, &(fdi->fd), sizeof(fdi->fd), nbytes, f) || scap_fd_read_prop_from_disk(handle, &(fdi->ino), sizeof(fdi->ino), nbytes, f) || scap_fd_read_prop_from_disk(handle, &type, sizeof(uint8_t), nbytes, f)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not read prop block for fd"); return SCAP_FAILURE; } // If new parameters are added, sub_len can be used to // see if they are available in the current capture. // For example, for a 32bit parameter: // // if(sub_len && (*nbytes + sizeof(uint32_t)) <= sub_len) // { // ... // } fdi->type = (scap_fd_type)type; switch(fdi->type) { case SCAP_FD_IPV4_SOCK: if(gzread(f, &(fdi->info.ipv4info.sip), sizeof(uint32_t)) != sizeof(uint32_t) || gzread(f, &(fdi->info.ipv4info.dip), sizeof(uint32_t)) != sizeof(uint32_t) || gzread(f, &(fdi->info.ipv4info.sport), sizeof(uint16_t)) != sizeof(uint16_t) || gzread(f, &(fdi->info.ipv4info.dport), sizeof(uint16_t)) != sizeof(uint16_t) || gzread(f, &(fdi->info.ipv4info.l4proto), sizeof(uint8_t)) != sizeof(uint8_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (1)"); return SCAP_FAILURE; } (*nbytes) += (sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint8_t)); break; case SCAP_FD_IPV4_SERVSOCK: if(gzread(f, &(fdi->info.ipv4serverinfo.ip), sizeof(uint32_t)) != sizeof(uint32_t) || gzread(f, &(fdi->info.ipv4serverinfo.port), sizeof(uint16_t)) != sizeof(uint16_t) || gzread(f, &(fdi->info.ipv4serverinfo.l4proto), sizeof(uint8_t)) != sizeof(uint8_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (2)"); return SCAP_FAILURE; } (*nbytes) += (sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint8_t)); break; case SCAP_FD_IPV6_SOCK: if(gzread(f, (char*)fdi->info.ipv6info.sip, sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4 || gzread(f, (char*)fdi->info.ipv6info.dip, sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4 || gzread(f, &(fdi->info.ipv6info.sport), sizeof(uint16_t)) != sizeof(uint16_t) || gzread(f, &(fdi->info.ipv6info.dport), sizeof(uint16_t)) != sizeof(uint16_t) || gzread(f, &(fdi->info.ipv6info.l4proto), sizeof(uint8_t)) != sizeof(uint8_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi3)"); } (*nbytes) += (sizeof(uint32_t) * 4 + // sip sizeof(uint32_t) * 4 + // dip sizeof(uint16_t) + // sport sizeof(uint16_t) + // dport sizeof(uint8_t)); // l4proto break; case SCAP_FD_IPV6_SERVSOCK: if(gzread(f, (char*)fdi->info.ipv6serverinfo.ip, sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4|| gzread(f, &(fdi->info.ipv6serverinfo.port), sizeof(uint16_t)) != sizeof(uint16_t) || gzread(f, &(fdi->info.ipv6serverinfo.l4proto), sizeof(uint8_t)) != sizeof(uint8_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi4)"); } (*nbytes) += (sizeof(uint32_t) * 4 + // ip sizeof(uint16_t) + // port sizeof(uint8_t)); // l4proto break; case SCAP_FD_UNIX_SOCK: if(gzread(f, &(fdi->info.unix_socket_info.source), sizeof(uint64_t)) != sizeof(uint64_t) || gzread(f, &(fdi->info.unix_socket_info.destination), sizeof(uint64_t)) != sizeof(uint64_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (fi5)"); return SCAP_FAILURE; } (*nbytes) += (sizeof(uint64_t) + sizeof(uint64_t)); res = scap_fd_read_fname_from_disk(handle, fdi->info.unix_socket_info.fname, nbytes, f); break; case SCAP_FD_FILE_V2: if(gzread(f, &(fdi->info.regularinfo.open_flags), sizeof(uint32_t)) != sizeof(uint32_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (fi1)"); return SCAP_FAILURE; } (*nbytes) += sizeof(uint32_t); res = scap_fd_read_fname_from_disk(handle, fdi->info.regularinfo.fname, nbytes, f); if (!sub_len || (sub_len < *nbytes + sizeof(uint32_t))) { break; } if(gzread(f, &(fdi->info.regularinfo.dev), sizeof(uint32_t)) != sizeof(uint32_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file (dev)"); return SCAP_FAILURE; } (*nbytes) += sizeof(uint32_t); break; case SCAP_FD_FIFO: case SCAP_FD_FILE: case SCAP_FD_DIRECTORY: case SCAP_FD_UNSUPPORTED: case SCAP_FD_EVENT: case SCAP_FD_SIGNALFD: case SCAP_FD_EVENTPOLL: case SCAP_FD_INOTIFY: case SCAP_FD_TIMERFD: case SCAP_FD_NETLINK: res = scap_fd_read_fname_from_disk(handle, fdi->info.fname,nbytes,f); break; case SCAP_FD_UNKNOWN: ASSERT(false); break; default: snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading the fd info from file, wrong fd type %u", (uint32_t)fdi->type); return SCAP_FAILURE; } if(sub_len && *nbytes != sub_len) { if(*nbytes > sub_len) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Had read %zu bytes, but fdlist entry have length %u.", *nbytes, sub_len); return SCAP_FAILURE; } toread = sub_len - *nbytes; fseekres = (int)gzseek(f, (long)toread, SEEK_CUR); if(fseekres == -1) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Can't skip %u bytes.", (unsigned int)toread); return SCAP_FAILURE; } *nbytes = sub_len; } return res; } void scap_fd_free_ns_sockets_list(scap_t *handle, struct scap_ns_socket_list **sockets) { struct scap_ns_socket_list *fdi; struct scap_ns_socket_list *tfdi; if(*sockets) { HASH_ITER(hh, *sockets, fdi, tfdi) { HASH_DEL(*sockets, fdi); scap_fd_free_table(handle, &fdi->sockets); free(fdi); } *sockets = NULL; } } void scap_fd_free_table(scap_t *handle, scap_fdinfo **fds) { struct scap_fdinfo *fdi; struct scap_fdinfo *tfdi; if(*fds) { HASH_ITER(hh, *fds, fdi, tfdi) { HASH_DEL(*fds, fdi); free(fdi); } *fds = NULL; } } void scap_fd_free_proc_fd_table(scap_t *handle, scap_threadinfo *tinfo) { if(tinfo->fdlist) { scap_fd_free_table(handle, &tinfo->fdlist); } } // // remove an fd from a process table // void scap_fd_remove(scap_t *handle, scap_threadinfo *tinfo, int64_t fd) { scap_fdinfo *fdi; // // Find the fd descriptor // HASH_FIND_INT64(tinfo->fdlist, &(fd), fdi); if(fdi == NULL) { // // Looks like there's no fd to remove. // Likely, the fd creation event was dropped. // //scap_proc_print_info(handle, tinfo); // ASSERT(false); return; } HASH_DEL(tinfo->fdlist, fdi); free(fdi); } // // Add the file descriptor info pointed by fdi to the fd table for process tinfo. // Note: silently skips if fdi->type is SCAP_FD_UNKNOWN. // int32_t scap_add_fd_to_proc_table(scap_t *handle, scap_threadinfo *tinfo, scap_fdinfo *fdi, char *error) { int32_t uth_status = SCAP_SUCCESS; scap_fdinfo *tfdi; // // Make sure this fd doesn't already exist // HASH_FIND_INT64(tinfo->fdlist, &(fdi->fd), tfdi); if(tfdi != NULL) { // // This can happen if: // - a close() has been dropped when capturing // - an fd has been closed by clone() or execve() (it happens when the fd is opened with the FD_CLOEXEC flag, // which we don't currently parse. // In either case, removing the old fd, replacing it with the new one and keeping going is a reasonable // choice. // HASH_DEL(tinfo->fdlist, tfdi); free(tfdi); } // // Add the fd to the table, or fire the notification callback // if(handle->m_proc_callback == NULL) { HASH_ADD_INT64(tinfo->fdlist, fd, fdi); if(uth_status != SCAP_SUCCESS) { snprintf(error, SCAP_LASTERR_SIZE, "process table allocation error (2)"); return SCAP_FAILURE; } } else { handle->m_proc_callback(handle->m_proc_callback_context, handle, tinfo->tid, tinfo, fdi); } return SCAP_SUCCESS; } // // Delete a device entry // void scap_dev_delete(scap_t* handle, scap_mountinfo* dev) { // // First, remove the process descriptor from the table // HASH_DEL(handle->m_dev_list, dev); // // Second, free the memory // free(dev); } // // Free the device table // void scap_free_device_table(scap_t* handle) { scap_mountinfo *dev, *tdev; HASH_ITER(hh, handle->m_dev_list, dev, tdev) { scap_dev_delete(handle, dev); } } #if defined(HAS_CAPTURE) int32_t scap_fd_handle_pipe(scap_t *handle, char *fname, scap_threadinfo *tinfo, scap_fdinfo *fdi, char *error) { char link_name[SCAP_MAX_PATH_SIZE]; ssize_t r; uint64_t ino; struct stat sb; r = readlink(fname, link_name, SCAP_MAX_PATH_SIZE); if (r <= 0) { snprintf(error, SCAP_LASTERR_SIZE, "Could not read link %s (%s)", fname, scap_strerror(handle, errno)); return SCAP_FAILURE; } link_name[r] = '\0'; if(1 != sscanf(link_name, "pipe:[%"PRIi64"]", &ino)) { // in this case we've got a named pipe // and we've got to call stat on the link name if(-1 == stat(link_name, &sb)) { return SCAP_SUCCESS; } ino = sb.st_ino; } strncpy(fdi->info.fname, link_name, SCAP_MAX_PATH_SIZE); fdi->ino = ino; return scap_add_fd_to_proc_table(handle, tinfo, fdi, error); } static inline uint32_t open_flags_to_scap(unsigned long flags) { uint32_t res = 0; switch (flags & (O_RDONLY | O_WRONLY | O_RDWR)) { case O_WRONLY: res |= PPM_O_WRONLY; break; case O_RDWR: res |= PPM_O_RDWR; break; default: res |= PPM_O_RDONLY; break; } if (flags & O_CREAT) res |= PPM_O_CREAT; if (flags & O_APPEND) res |= PPM_O_APPEND; #ifdef O_DSYNC if (flags & O_DSYNC) res |= PPM_O_DSYNC; #endif if (flags & O_EXCL) res |= PPM_O_EXCL; if (flags & O_NONBLOCK) res |= PPM_O_NONBLOCK; if (flags & O_SYNC) res |= PPM_O_SYNC; if (flags & O_TRUNC) res |= PPM_O_TRUNC; #ifdef O_DIRECT if (flags & O_DIRECT) res |= PPM_O_DIRECT; #endif #ifdef O_DIRECTORY if (flags & O_DIRECTORY) res |= PPM_O_DIRECTORY; #endif #ifdef O_LARGEFILE if (flags & O_LARGEFILE) res |= PPM_O_LARGEFILE; #endif #ifdef O_CLOEXEC if (flags & O_CLOEXEC) res |= PPM_O_CLOEXEC; #endif return res; } static uint32_t scap_get_device_by_mount_id(scap_t *handle, const char *procdir, unsigned long requested_mount_id) { char fd_dir_name[SCAP_MAX_PATH_SIZE]; char line[SCAP_MAX_PATH_SIZE]; FILE *finfo; scap_mountinfo *mountinfo; HASH_FIND_INT64(handle->m_dev_list, &requested_mount_id, mountinfo); if(mountinfo != NULL) { return mountinfo->dev; } snprintf(fd_dir_name, SCAP_MAX_PATH_SIZE, "%smountinfo", procdir); finfo = fopen(fd_dir_name, "r"); if(finfo == NULL) { return 0; } while(fgets(line, sizeof(line), finfo) != NULL) { uint32_t mount_id, major, minor; if(sscanf(line, "%u %*u %u:%u", &mount_id, &major, &minor) != 3) { continue; } if(mount_id == requested_mount_id) { uint32_t dev = makedev(major, minor); mountinfo = malloc(sizeof(*mountinfo)); if(mountinfo) { int32_t uth_status = SCAP_SUCCESS; mountinfo->mount_id = mount_id; mountinfo->dev = dev; HASH_ADD_INT64(handle->m_dev_list, mount_id, mountinfo); if(uth_status != SCAP_SUCCESS) { free(mountinfo); } } fclose(finfo); return dev; } } fclose(finfo); return 0; } void scap_fd_flags_file(scap_t *handle, scap_fdinfo *fdi, const char *procdir) { char fd_dir_name[SCAP_MAX_PATH_SIZE]; char line[SCAP_MAX_PATH_SIZE]; FILE *finfo; snprintf(fd_dir_name, SCAP_MAX_PATH_SIZE, "%sfdinfo/%" PRId64, procdir, fdi->fd); finfo = fopen(fd_dir_name, "r"); if(finfo == NULL) { return; } while(fgets(line, sizeof(line), finfo) != NULL) { // We are interested in the flags and the mnt_id. // // The format of the file is: // pos: XXXX // flags: YYYYYYYY // mnt_id: ZZZ if(!strncmp(line, "flags:\t", sizeof("flags:\t") - 1)) { uint32_t open_flags; errno = 0; unsigned long flags = strtoul(line + sizeof("flags:\t") - 1, NULL, 8); if(errno == ERANGE) { open_flags = PPM_O_NONE; } else { open_flags = open_flags_to_scap(flags); } fdi->info.regularinfo.open_flags = open_flags; } else if(!strncmp(line, "mnt_id:\t", sizeof("mnt_id:\t") - 1)) { uint32_t dev; errno = 0; unsigned long mount_id = strtoul(line + sizeof("mnt_id:\t") - 1, NULL, 10); if(errno == ERANGE) { dev = 0; } else { dev = scap_get_device_by_mount_id(handle, procdir, mount_id); } fdi->info.regularinfo.dev = dev; } } fclose(finfo); } int32_t scap_fd_handle_regular_file(scap_t *handle, char *fname, scap_threadinfo *tinfo, scap_fdinfo *fdi, const char *procdir, char *error) { char link_name[SCAP_MAX_PATH_SIZE]; ssize_t r; r = readlink(fname, link_name, SCAP_MAX_PATH_SIZE); if (r <= 0) { return SCAP_SUCCESS; } link_name[r] = '\0'; if(SCAP_FD_UNSUPPORTED == fdi->type) { // try to classify by link name if(0 == strcmp(link_name,"anon_inode:[eventfd]")) { fdi->type = SCAP_FD_EVENT; } else if(0 == strcmp(link_name,"anon_inode:[signalfd]")) { fdi->type = SCAP_FD_SIGNALFD; } else if(0 == strcmp(link_name,"anon_inode:[eventpoll]")) { fdi->type = SCAP_FD_EVENTPOLL; } else if(0 == strcmp(link_name,"anon_inode:inotify")) { fdi->type = SCAP_FD_INOTIFY; } else if(0 == strcmp(link_name,"anon_inode:[timerfd]")) { fdi->type = SCAP_FD_TIMERFD; } if(SCAP_FD_UNSUPPORTED == fdi->type) { // still not able to classify // printf("unsupported %s -> %s\n",fname,link_name); } fdi->info.fname[0] = '\0'; } else if(fdi->type == SCAP_FD_FILE_V2) { scap_fd_flags_file(handle, fdi, procdir); strncpy(fdi->info.regularinfo.fname, link_name, SCAP_MAX_PATH_SIZE); } else { strncpy(fdi->info.fname, link_name, SCAP_MAX_PATH_SIZE); } return scap_add_fd_to_proc_table(handle, tinfo, fdi, error); } int32_t scap_fd_handle_socket(scap_t *handle, char *fname, scap_threadinfo *tinfo, scap_fdinfo *fdi, char* procdir, uint64_t net_ns, struct scap_ns_socket_list **sockets_by_ns, char *error) { char link_name[SCAP_MAX_PATH_SIZE]; ssize_t r; scap_fdinfo *tfdi; uint64_t ino; struct scap_ns_socket_list* sockets = NULL; int32_t uth_status = SCAP_SUCCESS; if(*sockets_by_ns == (void*)-1) { return SCAP_SUCCESS; } else { HASH_FIND_INT64(*sockets_by_ns, &net_ns, sockets); if(sockets == NULL) { sockets = malloc(sizeof(struct scap_ns_socket_list)); sockets->net_ns = net_ns; sockets->sockets = NULL; char fd_error[SCAP_LASTERR_SIZE]; HASH_ADD_INT64(*sockets_by_ns, net_ns, sockets); if(uth_status != SCAP_SUCCESS) { snprintf(error, SCAP_LASTERR_SIZE, "socket list allocation error"); return SCAP_FAILURE; } if(scap_fd_read_sockets(handle, procdir, sockets, fd_error) == SCAP_FAILURE) { snprintf(error, SCAP_LASTERR_SIZE, "Cannot read sockets (%s)", fd_error); sockets->sockets = NULL; return SCAP_FAILURE; } } } r = readlink(fname, link_name, SCAP_MAX_PATH_SIZE); if(r <= 0) { return SCAP_SUCCESS; } link_name[r] = '\0'; strncpy(fdi->info.fname, link_name, SCAP_MAX_PATH_SIZE); // link name for sockets should be of the format socket:[ino] if(1 != sscanf(link_name, "socket:[%"PRIi64"]", &ino)) { // it's a kind of socket, but we don't support it right now fdi->type = SCAP_FD_UNSUPPORTED; return scap_add_fd_to_proc_table(handle, tinfo, fdi, error); } // // Lookup ino in the list of sockets // HASH_FIND_INT64(sockets->sockets, &ino, tfdi); if(tfdi != NULL) { memcpy(&(fdi->info), &(tfdi->info), sizeof(fdi->info)); fdi->ino = ino; fdi->type = tfdi->type; return scap_add_fd_to_proc_table(handle, tinfo, fdi, error); } else { return SCAP_SUCCESS; } } int32_t scap_fd_read_unix_sockets_from_proc_fs(scap_t *handle, const char* filename, scap_fdinfo **sockets) { FILE *f; char line[SCAP_MAX_PATH_SIZE]; int first_line = false; char *delimiters = " \t"; char *token; int32_t uth_status = SCAP_SUCCESS; f = fopen(filename, "r"); if(NULL == f) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not open sockets file %s (%s)", filename, scap_strerror(handle, errno)); return SCAP_FAILURE; } while(NULL != fgets(line, sizeof(line), f)) { char *scratch; // skip the first line ... contains field names if(!first_line) { first_line = true; continue; } scap_fdinfo *fdinfo = malloc(sizeof(scap_fdinfo)); fdinfo->type = SCAP_FD_UNIX_SOCK; // // parse the fields // // 1. Num token = strtok_r(line, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } fdinfo->info.unix_socket_info.source = strtoul(token, NULL, 16); fdinfo->info.unix_socket_info.destination = 0; // 2. RefCount token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 3. Protocol token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 4. Flags token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 5. Type token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 6. St token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 7. Inode token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } sscanf(token, "%"PRIu64, &(fdinfo->ino)); // 8. Path token = strtok_r(NULL, delimiters, &scratch); if(NULL != token) { strncpy(fdinfo->info.unix_socket_info.fname, token, SCAP_MAX_PATH_SIZE); } else { fdinfo->info.unix_socket_info.fname[0] = '\0'; } HASH_ADD_INT64((*sockets), ino, fdinfo); if(uth_status != SCAP_SUCCESS) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "unix socket allocation error"); fclose(f); return SCAP_FAILURE; } } fclose(f); return uth_status; } //sk Eth Pid Groups Rmem Wmem Dump Locks Drops Inode //ffff88011abfb000 0 0 00000000 0 0 0 2 0 13 int32_t scap_fd_read_netlink_sockets_from_proc_fs(scap_t *handle, const char* filename, scap_fdinfo **sockets) { FILE *f; char line[SCAP_MAX_PATH_SIZE]; int first_line = false; char *delimiters = " \t"; char *token; int32_t uth_status = SCAP_SUCCESS; f = fopen(filename, "r"); if(NULL == f) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not open netlink sockets file %s (%s)", filename, scap_strerror(handle, errno)); return SCAP_FAILURE; } while(NULL != fgets(line, sizeof(line), f)) { char *scratch; // skip the first line ... contains field names if(!first_line) { first_line = true; continue; } scap_fdinfo *fdinfo = malloc(sizeof(scap_fdinfo)); memset(fdinfo, 0, sizeof(scap_fdinfo)); fdinfo->type = SCAP_FD_UNIX_SOCK; // // parse the fields // // 1. Num token = strtok_r(line, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 2. Eth token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 3. Pid token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 4. Groups token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 5. Rmem token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 6. Wmem token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 7. Dump token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 8. Locks token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 9. Drops token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 10. Inode token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } sscanf(token, "%"PRIu64, &(fdinfo->ino)); HASH_ADD_INT64((*sockets), ino, fdinfo); if(uth_status != SCAP_SUCCESS) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "netlink socket allocation error"); fclose(f); return SCAP_FAILURE; } } fclose(f); return uth_status; } int32_t scap_fd_read_ipv4_sockets_from_proc_fs(scap_t *handle, const char *dir, int l4proto, scap_fdinfo **sockets) { FILE *f; int32_t uth_status = SCAP_SUCCESS; char* scan_buf; char* scan_pos; char* tmp_pos; uint32_t rsize; char* end; char tc; uint32_t j; scan_buf = (char*)malloc(SOCKET_SCAN_BUFFER_SIZE); if(scan_buf == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scan_buf allocation error"); return SCAP_FAILURE; } f = fopen(dir, "r"); if(NULL == f) { ASSERT(false); free(scan_buf); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not open ipv4 sockets dir %s (%s)", dir, scap_strerror(handle, errno)); return SCAP_FAILURE; } while((rsize = fread(scan_buf, 1, SOCKET_SCAN_BUFFER_SIZE, f)) != 0) { char* scan_end = scan_buf + rsize; scan_pos = scan_buf; while(scan_pos <= scan_end) { scan_pos = memchr(scan_pos, '\n', scan_end - scan_pos); if(scan_pos == NULL) { break; } scap_fdinfo *fdinfo = malloc(sizeof(scap_fdinfo)); // // Skip the sl field // scan_pos = memchr(scan_pos, ':', scan_end - scan_pos); if(scan_pos == NULL) { free(fdinfo); break; } scan_pos += 2; if(scan_pos + 80 >= scan_end) { free(fdinfo); break; } // // Scan the local address // tc = *(scan_pos + 8); *(scan_pos + 8) = 0; fdinfo->info.ipv4info.sip = strtoul(scan_pos, &end, 16); *(scan_pos + 8) = tc; scan_pos += 9; tc = *(scan_pos + 4); ASSERT(tc == ' '); *(scan_pos + 4) = 0; fdinfo->info.ipv4info.sport = (uint16_t)strtoul(scan_pos, &end, 16); *(scan_pos + 4) = tc; // // Scan the remote address // scan_pos += 5; tc = *(scan_pos + 8); *(scan_pos + 8) = 0; fdinfo->info.ipv4info.dip = strtoul(scan_pos, &end, 16); *(scan_pos + 8) = tc; scan_pos += 9; tc = *(scan_pos + 4); ASSERT(tc == ' '); *(scan_pos + 4) = 0; fdinfo->info.ipv4info.dport = (uint16_t)strtoul(scan_pos, &end, 16); *(scan_pos + 4) = tc; // // Skip to parsing the inode // scan_pos += 4; for(j = 0; j < 6; j++) { scan_pos++; scan_pos = memchr(scan_pos, ' ', scan_end - scan_pos); if(scan_pos == NULL) { break; } while(*scan_pos == ' ' && scan_pos < scan_end) { scan_pos++; } if(scan_pos >= scan_end) { break; } } if(j < 6) { free(fdinfo); break; } tmp_pos = scan_pos; scan_pos = memchr(scan_pos, ' ', scan_end - scan_pos); if(scan_pos == NULL || scan_pos >= scan_end) { free(fdinfo); break; } tc = *(scan_pos); fdinfo->ino = (uint64_t)strtoull(tmp_pos, &end, 10); *(scan_pos) = tc; // // Add to the table // if(fdinfo->info.ipv4info.dip == 0) { fdinfo->type = SCAP_FD_IPV4_SERVSOCK; fdinfo->info.ipv4serverinfo.l4proto = l4proto; fdinfo->info.ipv4serverinfo.port = fdinfo->info.ipv4info.sport; fdinfo->info.ipv4serverinfo.ip = fdinfo->info.ipv4info.sip; } else { fdinfo->type = SCAP_FD_IPV4_SOCK; fdinfo->info.ipv4info.l4proto = l4proto; } HASH_ADD_INT64((*sockets), ino, fdinfo); if(uth_status != SCAP_SUCCESS) { uth_status = SCAP_FAILURE; snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "ipv4 socket allocation error"); break; } scan_pos++; } } fclose(f); free(scan_buf); return uth_status; } int32_t scap_fd_is_ipv6_server_socket(uint32_t ip6_addr[4]) { return 0 == ip6_addr[0] && 0 == ip6_addr[1] && 0 == ip6_addr[2] && 0 == ip6_addr[3]; } int32_t scap_fd_read_ipv6_sockets_from_proc_fs(scap_t *handle, char *dir, int l4proto, scap_fdinfo **sockets) { FILE *f; int32_t uth_status = SCAP_SUCCESS; char* scan_buf; char* scan_pos; char* tmp_pos; uint32_t rsize; char* end; char tc; uint32_t j; scan_buf = (char*)malloc(SOCKET_SCAN_BUFFER_SIZE); if(scan_buf == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scan_buf allocation error"); return SCAP_FAILURE; } f = fopen(dir, "r"); if(NULL == f) { ASSERT(false); free(scan_buf); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not open ipv6 sockets dir %s (%s)", dir, scap_strerror(handle, errno)); return SCAP_FAILURE; } while((rsize = fread(scan_buf, 1, SOCKET_SCAN_BUFFER_SIZE, f)) != 0) { char* scan_end = scan_buf + rsize; scan_pos = scan_buf; while(scan_pos <= scan_end) { scan_pos = memchr(scan_pos, '\n', scan_end - scan_pos); if(scan_pos == NULL) { break; } scap_fdinfo *fdinfo = malloc(sizeof(scap_fdinfo)); // // Skip the sl field // scan_pos = memchr(scan_pos, ':', scan_end - scan_pos); if(scan_pos == NULL) { free(fdinfo); break; } scan_pos += 2; if(scan_pos + 80 >= scan_end) { free(fdinfo); break; } // // Scan the first address // tc = *(scan_pos + 8); *(scan_pos + 8) = 0; fdinfo->info.ipv6info.sip[0] = strtoul(scan_pos, &end, 16); *(scan_pos + 8) = tc; scan_pos += 8; tc = *(scan_pos + 8); *(scan_pos + 8) = 0; fdinfo->info.ipv6info.sip[1] = strtoul(scan_pos, &end, 16); *(scan_pos + 8) = tc; scan_pos += 8; tc = *(scan_pos + 8); *(scan_pos + 8) = 0; fdinfo->info.ipv6info.sip[2] = strtoul(scan_pos, &end, 16); *(scan_pos + 8) = tc; scan_pos += 8; tc = *(scan_pos + 8); ASSERT(tc == ':'); *(scan_pos + 8) = 0; fdinfo->info.ipv6info.sip[3] = strtoul(scan_pos, &end, 16); *(scan_pos + 8) = tc; scan_pos += 9; tc = *(scan_pos + 4); ASSERT(tc == ' '); *(scan_pos + 4) = 0; fdinfo->info.ipv6info.sport = (uint16_t)strtoul(scan_pos, &end, 16); *(scan_pos + 4) = tc; // // Scan the second address // scan_pos += 5; tc = *(scan_pos + 8); *(scan_pos + 8) = 0; fdinfo->info.ipv6info.dip[0] = strtoul(scan_pos, &end, 16); *(scan_pos + 8) = tc; scan_pos += 8; tc = *(scan_pos + 8); *(scan_pos + 8) = 0; fdinfo->info.ipv6info.dip[1] = strtoul(scan_pos, &end, 16); *(scan_pos + 8) = tc; scan_pos += 8; tc = *(scan_pos + 8); *(scan_pos + 8) = 0; fdinfo->info.ipv6info.dip[2] = strtoul(scan_pos, &end, 16); *(scan_pos + 8) = tc; scan_pos += 8; tc = *(scan_pos + 8); ASSERT(tc == ':'); *(scan_pos + 8) = 0; fdinfo->info.ipv6info.dip[3] = strtoul(scan_pos, &end, 16); *(scan_pos + 8) = tc; scan_pos += 9; tc = *(scan_pos + 4); ASSERT(tc == ' '); *(scan_pos + 4) = 0; fdinfo->info.ipv6info.dport = (uint16_t)strtoul(scan_pos, &end, 16); *(scan_pos + 4) = tc; // // Skip to parsing the inode // scan_pos += 4; for(j = 0; j < 6; j++) { scan_pos++; scan_pos = memchr(scan_pos, ' ', scan_end - scan_pos); if(scan_pos == NULL) { break; } while(*scan_pos == ' ' && scan_pos < scan_end) { scan_pos++; } if(scan_pos >= scan_end) { break; } } if(j < 6) { free(fdinfo); break; } tmp_pos = scan_pos; scan_pos = memchr(scan_pos, ' ', scan_end - scan_pos); if(scan_pos == NULL || scan_pos >= scan_end) { free(fdinfo); break; } tc = *(scan_pos); fdinfo->ino = (uint64_t)strtoull(tmp_pos, &end, 10); *(scan_pos) = tc; // // Add to the table // if(scap_fd_is_ipv6_server_socket(fdinfo->info.ipv6info.dip)) { fdinfo->type = SCAP_FD_IPV6_SERVSOCK; fdinfo->info.ipv6serverinfo.l4proto = l4proto; fdinfo->info.ipv6serverinfo.port = fdinfo->info.ipv6info.sport; fdinfo->info.ipv6serverinfo.ip[0] = fdinfo->info.ipv6info.sip[0]; fdinfo->info.ipv6serverinfo.ip[1] = fdinfo->info.ipv6info.sip[1]; fdinfo->info.ipv6serverinfo.ip[2] = fdinfo->info.ipv6info.sip[2]; fdinfo->info.ipv6serverinfo.ip[3] = fdinfo->info.ipv6info.sip[3]; } else { fdinfo->type = SCAP_FD_IPV6_SOCK; fdinfo->info.ipv6info.l4proto = l4proto; } HASH_ADD_INT64((*sockets), ino, fdinfo); if(uth_status != SCAP_SUCCESS) { uth_status = SCAP_FAILURE; snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "ipv6 socket allocation error"); break; } scan_pos++; } } fclose(f); free(scan_buf); return uth_status; } int32_t scap_fd_read_sockets(scap_t *handle, char* procdir, struct scap_ns_socket_list *sockets, char *error) { char filename[SCAP_MAX_PATH_SIZE]; char netroot[SCAP_MAX_PATH_SIZE]; if(sockets->net_ns) { // // Namespace support, look in /proc/PID/net/ // snprintf(netroot, sizeof(netroot), "%snet/", procdir); } else { // // No namespace support, look in the base /proc // snprintf(netroot, sizeof(netroot), "%s/proc/net/", scap_get_host_root()); } snprintf(filename, sizeof(filename), "%stcp", netroot); if(scap_fd_read_ipv4_sockets_from_proc_fs(handle, filename, SCAP_L4_TCP, &sockets->sockets) == SCAP_FAILURE) { scap_fd_free_table(handle, &sockets->sockets); snprintf(error, SCAP_LASTERR_SIZE, "Could not read ipv4 tcp sockets (%s)", handle->m_lasterr); return SCAP_FAILURE; } snprintf(filename, sizeof(filename), "%sudp", netroot); if(scap_fd_read_ipv4_sockets_from_proc_fs(handle, filename, SCAP_L4_UDP, &sockets->sockets) == SCAP_FAILURE) { scap_fd_free_table(handle, &sockets->sockets); snprintf(error, SCAP_LASTERR_SIZE, "Could not read ipv4 udp sockets (%s)", handle->m_lasterr); return SCAP_FAILURE; } snprintf(filename, sizeof(filename), "%sraw", netroot); if(scap_fd_read_ipv4_sockets_from_proc_fs(handle, filename, SCAP_L4_RAW, &sockets->sockets) == SCAP_FAILURE) { scap_fd_free_table(handle, &sockets->sockets); snprintf(error, SCAP_LASTERR_SIZE, "Could not read ipv4 raw sockets (%s)", handle->m_lasterr); return SCAP_FAILURE; } snprintf(filename, sizeof(filename), "%sunix", netroot); if(scap_fd_read_unix_sockets_from_proc_fs(handle, filename, &sockets->sockets) == SCAP_FAILURE) { scap_fd_free_table(handle, &sockets->sockets); snprintf(error, SCAP_LASTERR_SIZE, "Could not read unix sockets (%s)", handle->m_lasterr); return SCAP_FAILURE; } snprintf(filename, sizeof(filename), "%snetlink", netroot); if(scap_fd_read_netlink_sockets_from_proc_fs(handle, filename, &sockets->sockets) == SCAP_FAILURE) { scap_fd_free_table(handle, &sockets->sockets); snprintf(error, SCAP_LASTERR_SIZE, "Could not read netlink sockets (%s)", handle->m_lasterr); return SCAP_FAILURE; } snprintf(filename, sizeof(filename), "%stcp6", netroot); /* We assume if there is /proc/net/tcp6 that ipv6 is available */ if(access(filename, R_OK) == 0) { if(scap_fd_read_ipv6_sockets_from_proc_fs(handle, filename, SCAP_L4_TCP, &sockets->sockets) == SCAP_FAILURE) { scap_fd_free_table(handle, &sockets->sockets); snprintf(error, SCAP_LASTERR_SIZE, "Could not read ipv6 tcp sockets (%s)", handle->m_lasterr); return SCAP_FAILURE; } snprintf(filename, sizeof(filename), "%sudp6", netroot); if(scap_fd_read_ipv6_sockets_from_proc_fs(handle, filename, SCAP_L4_UDP, &sockets->sockets) == SCAP_FAILURE) { scap_fd_free_table(handle, &sockets->sockets); snprintf(error, SCAP_LASTERR_SIZE, "Could not read ipv6 udp sockets (%s)", handle->m_lasterr); return SCAP_FAILURE; } snprintf(filename, sizeof(filename), "%sraw6", netroot); if(scap_fd_read_ipv6_sockets_from_proc_fs(handle, filename, SCAP_L4_RAW, &sockets->sockets) == SCAP_FAILURE) { scap_fd_free_table(handle, &sockets->sockets); snprintf(error, SCAP_LASTERR_SIZE, "Could not read ipv6 raw sockets (%s)", handle->m_lasterr); return SCAP_FAILURE; } } return SCAP_SUCCESS; } int32_t scap_fd_allocate_fdinfo(scap_t *handle, scap_fdinfo **fdi, int64_t fd, scap_fd_type type) { ASSERT(NULL == *fdi); *fdi = (scap_fdinfo *)malloc(sizeof(scap_fdinfo)); if(*fdi == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "fd table allocation error (2)"); return SCAP_FAILURE; } (*fdi)->type = type; (*fdi)->fd = fd; return SCAP_SUCCESS; } void scap_fd_free_fdinfo(scap_fdinfo **fdi) { if(NULL != *fdi) { free(*fdi); *fdi = NULL; } } char * decode_st_mode(struct stat* sb) { switch(sb->st_mode & S_IFMT) { case S_IFBLK: return "block device"; break; case S_IFCHR: return "character device"; break; case S_IFDIR: return "directory"; break; case S_IFIFO: return "FIFO/pipe"; break; case S_IFLNK: return "symlink"; break; case S_IFREG: return "regular file"; break; case S_IFSOCK: return "socket"; break; default: return "unknown?"; break; } } // // Scan the directory containing the fd's of a proc /proc/x/fd // int32_t scap_fd_scan_fd_dir(scap_t *handle, char *procdir, scap_threadinfo *tinfo, struct scap_ns_socket_list **sockets_by_ns, char *error) { DIR *dir_p; struct dirent *dir_entry_p; int32_t res = SCAP_SUCCESS; char fd_dir_name[SCAP_MAX_PATH_SIZE]; char f_name[SCAP_MAX_PATH_SIZE]; char link_name[SCAP_MAX_PATH_SIZE]; struct stat sb; uint64_t fd; scap_fdinfo *fdi = NULL; uint64_t net_ns; ssize_t r; uint16_t fd_added = 0; snprintf(fd_dir_name, SCAP_MAX_PATH_SIZE, "%sfd", procdir); dir_p = opendir(fd_dir_name); if(dir_p == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "error opening the directory %s", fd_dir_name); return SCAP_NOTFOUND; } // // Get the network namespace of the process // snprintf(f_name, sizeof(f_name), "%sns/net", procdir); r = readlink(f_name, link_name, sizeof(link_name)); if(r <= 0) { // // No network namespace available. Assume global // net_ns = 0; } else { link_name[r] = '\0'; sscanf(link_name, "net:[%"PRIi64"]", &net_ns); } while((dir_entry_p = readdir(dir_p)) != NULL && (handle->m_fd_lookup_limit == 0 || fd_added < handle->m_fd_lookup_limit)) { fdi = NULL; snprintf(f_name, SCAP_MAX_PATH_SIZE, "%s/%s", fd_dir_name, dir_entry_p->d_name); if(-1 == stat(f_name, &sb) || 1 != sscanf(dir_entry_p->d_name, "%"PRIu64, &fd)) { continue; } // In no driver mode to limit cpu usage we just parse sockets // because we are interested only on them if(handle->m_mode == SCAP_MODE_NODRIVER && !S_ISSOCK(sb.st_mode)) { continue; } switch(sb.st_mode & S_IFMT) { case S_IFIFO: res = scap_fd_allocate_fdinfo(handle, &fdi, fd, SCAP_FD_FIFO); if(SCAP_FAILURE == res) { snprintf(error, SCAP_LASTERR_SIZE, "can't allocate scap fd handle for fifo fd %" PRIu64, fd); break; } res = scap_fd_handle_pipe(handle, f_name, tinfo, fdi, error); break; case S_IFREG: case S_IFBLK: case S_IFCHR: case S_IFLNK: res = scap_fd_allocate_fdinfo(handle, &fdi, fd, SCAP_FD_FILE_V2); if(SCAP_FAILURE == res) { snprintf(error, SCAP_LASTERR_SIZE, "can't allocate scap fd handle for file fd %" PRIu64, fd); break; } fdi->ino = sb.st_ino; res = scap_fd_handle_regular_file(handle, f_name, tinfo, fdi, procdir, error); break; case S_IFDIR: res = scap_fd_allocate_fdinfo(handle, &fdi, fd, SCAP_FD_DIRECTORY); if(SCAP_FAILURE == res) { snprintf(error, SCAP_LASTERR_SIZE, "can't allocate scap fd handle for dir fd %" PRIu64, fd); break; } fdi->ino = sb.st_ino; res = scap_fd_handle_regular_file(handle, f_name, tinfo, fdi, procdir, error); break; case S_IFSOCK: res = scap_fd_allocate_fdinfo(handle, &fdi, fd, SCAP_FD_UNKNOWN); if(SCAP_FAILURE == res) { snprintf(error, SCAP_LASTERR_SIZE, "can't allocate scap fd handle for sock fd %" PRIu64, fd); break; } res = scap_fd_handle_socket(handle, f_name, tinfo, fdi, procdir, net_ns, sockets_by_ns, error); if(handle->m_proc_callback == NULL) { // we can land here if we've got a netlink socket if(fdi->type == SCAP_FD_UNKNOWN) { scap_fd_free_fdinfo(&fdi); } } break; default: res = scap_fd_allocate_fdinfo(handle, &fdi, fd, SCAP_FD_UNSUPPORTED); if(SCAP_FAILURE == res) { snprintf(error, SCAP_LASTERR_SIZE, "can't allocate scap fd handle for unsupported fd %" PRIu64, fd); break; } fdi->ino = sb.st_ino; res = scap_fd_handle_regular_file(handle, f_name, tinfo, fdi, procdir, error); break; } if(handle->m_proc_callback != NULL) { if(fdi) { scap_fd_free_fdinfo(&fdi); } } if(SCAP_SUCCESS != res) { break; } else { ++fd_added; } } closedir(dir_p); return res; } #endif // HAS_CAPTURE // // Internal helper function to output the fd table of a process // void scap_fd_print_table(scap_t *handle, scap_threadinfo *tinfo) { scap_fd_print_fd_table(handle, tinfo->fdlist); } void scap_fd_print_fd_table(scap_t *handle, scap_fdinfo *fds) { scap_fdinfo *fdi; scap_fdinfo *tfdi; char str[SCAP_MAX_PATH_SIZE]; HASH_ITER(hh, fds, fdi, tfdi) { if(scap_fd_info_to_string(handle, fdi, str, SCAP_MAX_PATH_SIZE) != SCAP_SUCCESS) { ASSERT(false); snprintf(str, SCAP_MAX_PATH_SIZE, "N.A."); } fprintf(stderr, " %"PRIu64") %s\n", fdi->fd, str); } } sysdig-0.26.4/userspace/libscap/scap_iflist.c000066400000000000000000000143721352731327100211730ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "scap.h" #include "scap-int.h" #if defined(HAS_CAPTURE) #include #include #include #include #include #include #include // // Allocate and return the list of interfaces on this system // int32_t scap_create_iflist(scap_t* handle) { struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; void *tempAddrPtr = NULL; int rc = 0; uint32_t ifcnt4 = 0; uint32_t ifcnt6 = 0; // // If the list of interfaces was already allocated for this handle (for example because this is // not the first interface list block), free it // if(handle->m_addrlist != NULL) { scap_free_iflist(handle->m_addrlist); handle->m_addrlist = NULL; } rc = getifaddrs(&interfaceArray); /* retrieve the current interfaces */ if(rc != 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "getifaddrs failed"); return SCAP_FAILURE; } // // First pass: count the number of interfaces // for(tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) { if(tempIfAddr->ifa_addr == NULL) { // "eql" interface like on EC2 continue; } if(tempIfAddr->ifa_addr->sa_family == AF_INET) { ifcnt4++; } else if(tempIfAddr->ifa_addr->sa_family == AF_INET6) { ifcnt6++; } } // // Allocate the handle and the arrays // handle->m_addrlist = (scap_addrlist*)malloc(sizeof(scap_addrlist)); if(!handle->m_addrlist) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "getifaddrs allocation failed(1)"); return SCAP_FAILURE; } if(ifcnt4 != 0) { handle->m_addrlist->v4list = (scap_ifinfo_ipv4*)malloc(ifcnt4 * sizeof(scap_ifinfo_ipv4)); if(!handle->m_addrlist->v4list) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "getifaddrs allocation failed(2)"); free(handle->m_addrlist); return SCAP_FAILURE; } } else { handle->m_addrlist->v4list = NULL; } if(ifcnt6 != 0) { handle->m_addrlist->v6list = (scap_ifinfo_ipv6*)malloc(ifcnt6 * sizeof(scap_ifinfo_ipv6)); if(!handle->m_addrlist->v6list) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "getifaddrs allocation failed(3)"); if(handle->m_addrlist->v4list) { free(handle->m_addrlist->v4list); } free(handle->m_addrlist); return SCAP_FAILURE; } } else { handle->m_addrlist->v6list = NULL; } handle->m_addrlist->n_v4_addrs = ifcnt4; handle->m_addrlist->n_v6_addrs = ifcnt6; // // Second pass: populate the arrays // handle->m_addrlist->totlen = 0; ifcnt4 = 0; ifcnt6 = 0; for(tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) { if(tempIfAddr->ifa_addr == NULL) { // "eql" interface like on EC2 continue; } if(tempIfAddr->ifa_addr->sa_family == AF_INET) { handle->m_addrlist->v4list[ifcnt4].type = SCAP_II_IPV4; tempAddrPtr = &((struct sockaddr_in *)tempIfAddr->ifa_addr)->sin_addr; handle->m_addrlist->v4list[ifcnt4].addr = *(uint32_t*)tempAddrPtr; if(tempIfAddr->ifa_netmask != NULL) { handle->m_addrlist->v4list[ifcnt4].netmask = *(uint32_t*)&(((struct sockaddr_in *)tempIfAddr->ifa_netmask)->sin_addr); } else { handle->m_addrlist->v4list[ifcnt4].netmask = 0; } #ifndef CYGWING_AGENT if(tempIfAddr->ifa_ifu.ifu_broadaddr != NULL) { handle->m_addrlist->v4list[ifcnt4].bcast = *(uint32_t*)&(((struct sockaddr_in *)tempIfAddr->ifa_ifu.ifu_broadaddr)->sin_addr); } else { handle->m_addrlist->v4list[ifcnt4].bcast = 0; } #else handle->m_addrlist->v4list[ifcnt4].bcast = 0; #endif strncpy(handle->m_addrlist->v4list[ifcnt4].ifname, tempIfAddr->ifa_name, SCAP_MAX_PATH_SIZE); handle->m_addrlist->v4list[ifcnt4].ifnamelen = strlen(tempIfAddr->ifa_name); handle->m_addrlist->v4list[ifcnt4].linkspeed = 0; handle->m_addrlist->totlen += (sizeof(scap_ifinfo_ipv4) + handle->m_addrlist->v4list[ifcnt4].ifnamelen - SCAP_MAX_PATH_SIZE); ifcnt4++; } else if(tempIfAddr->ifa_addr->sa_family == AF_INET6) { handle->m_addrlist->v6list[ifcnt6].type = SCAP_II_IPV6; tempAddrPtr = &((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_addr; memcpy(handle->m_addrlist->v6list[ifcnt6].addr, tempAddrPtr, 16); if(tempIfAddr->ifa_netmask != NULL) { memcpy(handle->m_addrlist->v6list[ifcnt6].netmask, &(((struct sockaddr_in6 *)tempIfAddr->ifa_netmask)->sin6_addr), 16); } else { memset(handle->m_addrlist->v6list[ifcnt6].netmask, 0, 16); } #ifndef CYGWING_AGENT if(tempIfAddr->ifa_ifu.ifu_broadaddr != NULL) { memcpy(handle->m_addrlist->v6list[ifcnt6].bcast, &(((struct sockaddr_in6 *)tempIfAddr->ifa_ifu.ifu_broadaddr)->sin6_addr), 16); } else { memset(handle->m_addrlist->v6list[ifcnt6].bcast, 0, 16); } #else handle->m_addrlist->v4list[ifcnt4].bcast = 0; #endif strncpy(handle->m_addrlist->v6list[ifcnt6].ifname, tempIfAddr->ifa_name, SCAP_MAX_PATH_SIZE); handle->m_addrlist->v6list[ifcnt6].ifnamelen = strlen(tempIfAddr->ifa_name); handle->m_addrlist->v6list[ifcnt6].linkspeed = 0; handle->m_addrlist->totlen += (sizeof(scap_ifinfo_ipv6) + handle->m_addrlist->v6list[ifcnt6].ifnamelen - SCAP_MAX_PATH_SIZE); ifcnt6++; } else { continue; } } // // Memory cleanup // freeifaddrs(interfaceArray); return SCAP_SUCCESS; } void scap_refresh_iflist(scap_t* handle) { scap_free_iflist(handle->m_addrlist); handle->m_addrlist = NULL; scap_create_iflist(handle); } #endif // HAS_CAPTURE // // Free a previously allocated list of interfaces // void scap_free_iflist(scap_addrlist* ifhandle) { if(ifhandle) { if(ifhandle->v6list) { free(ifhandle->v6list); } if(ifhandle->v4list) { free(ifhandle->v4list); } free(ifhandle); } } sysdig-0.26.4/userspace/libscap/scap_procs.c000066400000000000000000000774741352731327100210430ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #ifdef HAS_CAPTURE #ifndef CYGWING_AGENT #include #include #include #include #include #include #include #endif // CYGWING_AGENT #endif // HAS_CAPTURE #include "scap.h" #include "../../driver/ppm_ringbuffer.h" #include "scap-int.h" #ifdef CYGWING_AGENT #include "windows_hal.h" #endif #if defined(_WIN64) || defined(WIN64) || defined(_WIN32) || defined(WIN32) #define strerror_r(errnum, buf, size) strerror_s(buf, size, errnum) #endif #if defined(HAS_CAPTURE) #ifndef CYGWING_AGENT int32_t scap_proc_fill_cwd(scap_t *handle, char* procdirname, struct scap_threadinfo* tinfo) { int target_res; char filename[SCAP_MAX_PATH_SIZE]; snprintf(filename, sizeof(filename), "%scwd", procdirname); target_res = readlink(filename, tinfo->cwd, sizeof(tinfo->cwd) - 1); if(target_res <= 0) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "readlink %s failed (%s)", filename, scap_strerror(handle, errno)); return SCAP_FAILURE; } tinfo->cwd[target_res] = '\0'; return SCAP_SUCCESS; } int32_t scap_proc_fill_info_from_stats(scap_t *handle, char* procdirname, struct scap_threadinfo* tinfo) { char filename[SCAP_MAX_PATH_SIZE]; uint32_t nfound = 0; int64_t tmp; uint32_t uid; uint64_t tgid; uint64_t ppid; uint64_t vpid; uint64_t vtid; int64_t sid; int64_t pgid; int64_t vpgid; uint32_t vmsize_kb; uint32_t vmrss_kb; uint32_t vmswap_kb; uint64_t pfmajor; uint64_t pfminor; int32_t tty; char line[512]; char tmpc; char* s; tinfo->uid = (uint32_t)-1; tinfo->ptid = (uint32_t)-1LL; tinfo->sid = 0; tinfo->vpgid = 0; tinfo->vmsize_kb = 0; tinfo->vmrss_kb = 0; tinfo->vmswap_kb = 0; tinfo->pfmajor = 0; tinfo->pfminor = 0; tinfo->filtered_out = 0; tinfo->tty = 0; snprintf(filename, sizeof(filename), "%sstatus", procdirname); FILE* f = fopen(filename, "r"); if(f == NULL) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "open status file %s failed (%s)", filename, scap_strerror(handle, errno)); return SCAP_FAILURE; } while(fgets(line, sizeof(line), f) != NULL) { if(strstr(line, "Tgid") == line) { nfound++; if(sscanf(line, "Tgid: %" PRIu64, &tgid) == 1) { tinfo->pid = tgid; } else { ASSERT(false); } } if(strstr(line, "Uid") == line) { nfound++; if(sscanf(line, "Uid: %" PRIu64 " %" PRIu32, &tmp, &uid) == 2) { tinfo->uid = uid; } else { ASSERT(false); } } else if(strstr(line, "Gid") == line) { nfound++; if(sscanf(line, "Gid: %" PRIu64 " %" PRIu32, &tmp, &uid) == 2) { tinfo->gid = uid; } else { ASSERT(false); } } else if(strstr(line, "PPid") == line) { nfound++; if(sscanf(line, "PPid: %" PRIu64, &ppid) == 1) { tinfo->ptid = ppid; } else { ASSERT(false); } } else if(strstr(line, "VmSize:") == line) { nfound++; if(sscanf(line, "VmSize: %" PRIu32, &vmsize_kb) == 1) { tinfo->vmsize_kb = vmsize_kb; } else { ASSERT(false); } } else if(strstr(line, "VmRSS:") == line) { nfound++; if(sscanf(line, "VmRSS: %" PRIu32, &vmrss_kb) == 1) { tinfo->vmrss_kb = vmrss_kb; } else { ASSERT(false); } } else if(strstr(line, "VmSwap:") == line) { nfound++; if(sscanf(line, "VmSwap: %" PRIu32, &vmswap_kb) == 1) { tinfo->vmswap_kb = vmswap_kb; } else { ASSERT(false); } } else if(strstr(line, "NSpid:") == line) { nfound++; if(sscanf(line, "NSpid: %*u %" PRIu64, &vtid) == 1) { tinfo->vtid = vtid; } else { tinfo->vtid = tinfo->tid; } } else if(strstr(line, "NSpgid:") == line) { nfound++; if(sscanf(line, "NSpgid: %*u %" PRIu64, &vpgid) == 1) { tinfo->vpgid = vpgid; } } else if(strstr(line, "NStgid:") == line) { nfound++; if(sscanf(line, "NStgid: %*u %" PRIu64, &vpid) == 1) { tinfo->vpid = vpid; } else { tinfo->vpid = tinfo->pid; } } if(nfound == 10) { break; } } ASSERT(nfound == 10 || nfound == 7 || nfound == 6); fclose(f); snprintf(filename, sizeof(filename), "%sstat", procdirname); f = fopen(filename, "r"); if(f == NULL) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "read stat file %s failed (%s)", filename, scap_strerror(handle, errno)); return SCAP_FAILURE; } if(fgets(line, sizeof(line), f) == NULL) { ASSERT(false); fclose(f); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not read from stat file %s (%s)", filename, scap_strerror(handle, errno)); return SCAP_FAILURE; } s = strrchr(line, ')'); if(s == NULL) { ASSERT(false); fclose(f); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not find closng parens in stat file %s", filename); return SCAP_FAILURE; } // // Extract the line content // if(sscanf(s + 2, "%c %" PRId64 " %" PRId64 " %" PRId64 " %" PRId32 " %" PRId64 " %" PRId64 " %" PRId64 " %" PRId64 " %" PRId64, &tmpc, &tmp, &pgid, &sid, &tty, &tmp, &tmp, &pfminor, &tmp, &pfmajor) != 10) { ASSERT(false); fclose(f); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not read expected fields from stat file %s", filename); return SCAP_FAILURE; } tinfo->pfmajor = pfmajor; tinfo->pfminor = pfminor; tinfo->sid = (uint64_t) sid; // If we did not find vpgid above, set it to pgid from the // global namespace. if(tinfo->vpgid == 0) { tinfo->vpgid = pgid; } tinfo->tty = tty; fclose(f); return SCAP_SUCCESS; } // // use prlimit to extract the RLIMIT_NOFILE for the tid. On systems where prlimit // is not supported, just return -1 // static int32_t scap_proc_fill_flimit(scap_t *handle, uint64_t tid, struct scap_threadinfo* tinfo) #ifdef SYS_prlimit64 { struct rlimit rl; #ifdef __NR_prlimit64 if(syscall(SYS_prlimit64, tid, RLIMIT_NOFILE, NULL, &rl) == 0) { tinfo->fdlimit = rl.rlim_cur; return SCAP_SUCCESS; } #endif tinfo->fdlimit = -1; return SCAP_SUCCESS; } #else { tinfo->fdlimit = -1; return SCAP_SUCCESS; } #endif int32_t scap_proc_fill_cgroups(scap_t *handle, struct scap_threadinfo* tinfo, const char* procdirname) { char filename[SCAP_MAX_PATH_SIZE]; char line[SCAP_MAX_CGROUPS_SIZE]; tinfo->cgroups_len = 0; snprintf(filename, sizeof(filename), "%scgroup", procdirname); if(access(filename, R_OK) == -1) { return SCAP_SUCCESS; } FILE* f = fopen(filename, "r"); if(f == NULL) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "open cgroup file %s failed (%s)", filename, scap_strerror(handle, errno)); return SCAP_FAILURE; } while(fgets(line, sizeof(line), f) != NULL) { char* token; char* subsys_list; char* cgroup; char* scratch; // id token = strtok_r(line, ":", &scratch); if(token == NULL) { ASSERT(false); fclose(f); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Did not find id in cgroup file %s", filename); return SCAP_FAILURE; } // subsys subsys_list = strtok_r(NULL, ":", &scratch); if(subsys_list == NULL) { ASSERT(false); fclose(f); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Did not find subsys in cgroup file %s", filename); return SCAP_FAILURE; } // Hack to detect empty fields, because strtok does not support it // strsep() should be used to fix this but it's not available // on CentOS 6 (has been added from Glibc 2.19) if(subsys_list-token-strlen(token) > 1) { // skip cgroups like this: // 0::/init.scope continue; } // cgroup cgroup = strtok_r(NULL, ":", &scratch); if(cgroup == NULL) { ASSERT(false); fclose(f); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Did not find cgroup in cgroup file %s", filename); return SCAP_FAILURE; } // remove the \n cgroup[strlen(cgroup) - 1] = 0; while((token = strtok_r(subsys_list, ",", &scratch)) != NULL) { subsys_list = NULL; if(strlen(cgroup) + 1 + strlen(token) + 1 > SCAP_MAX_CGROUPS_SIZE - tinfo->cgroups_len) { ASSERT(false); fclose(f); return SCAP_SUCCESS; } snprintf(tinfo->cgroups + tinfo->cgroups_len, SCAP_MAX_CGROUPS_SIZE - tinfo->cgroups_len, "%s=%s", token, cgroup); tinfo->cgroups_len += strlen(cgroup) + 1 + strlen(token) + 1; } } fclose(f); return SCAP_SUCCESS; } static int32_t scap_get_vtid(scap_t* handle, int64_t tid, int64_t *vtid) { if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Cannot get vtid (not in live mode)"); return SCAP_FAILURE; } #if !defined(HAS_CAPTURE) ASSERT(false) return SCAP_FAILURE; #else if(handle->m_bpf) { *vtid = 0; } else { *vtid = ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_GET_VTID, tid); if(*vtid == -1) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "ioctl to get vtid failed (%s)", scap_strerror(handle, errno)); return SCAP_FAILURE; } } return SCAP_SUCCESS; #endif } static int32_t scap_get_vpid(scap_t* handle, int64_t tid, int64_t *vpid) { if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Cannot get vtid (not in live mode)"); return SCAP_FAILURE; } #if !defined(HAS_CAPTURE) ASSERT(false) return SCAP_FAILURE; #else if(handle->m_bpf) { *vpid = 0; } else { *vpid = ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_GET_VPID, tid); if(*vpid == -1) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "ioctl to get vpid failed (%s)", scap_strerror(handle, errno)); return SCAP_FAILURE; } } return SCAP_SUCCESS; #endif } int32_t scap_proc_fill_root(scap_t *handle, struct scap_threadinfo* tinfo, const char* procdirname) { char root_path[SCAP_MAX_PATH_SIZE]; snprintf(root_path, sizeof(root_path), "%sroot", procdirname); if ( readlink(root_path, tinfo->root, sizeof(tinfo->root)) > 0) { return SCAP_SUCCESS; } else { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "readlink %s failed (%s)", root_path, scap_strerror(handle, errno)); return SCAP_FAILURE; } } int32_t scap_proc_fill_loginuid(scap_t *handle, struct scap_threadinfo* tinfo, const char* procdirname) { uint32_t loginuid; char loginuid_path[SCAP_MAX_PATH_SIZE]; char line[512]; snprintf(loginuid_path, sizeof(loginuid_path), "%sloginuid", procdirname); FILE* f = fopen(loginuid_path, "r"); if(f == NULL) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Open loginuid file %s failed (%s)", loginuid_path, scap_strerror(handle, errno)); return SCAP_FAILURE; } if (fgets(line, sizeof(line), f) == NULL) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not read loginuid from %s (%s)", loginuid_path, scap_strerror(handle, errno)); fclose(f); return SCAP_FAILURE; } fclose(f); if(sscanf(line, "%" PRId32, &loginuid) == 1) { tinfo->loginuid = loginuid; return SCAP_SUCCESS; } else { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not read loginuid from %s", loginuid_path); return SCAP_FAILURE; } } // // Add a process to the list by parsing its entry under /proc // static int32_t scap_proc_add_from_proc(scap_t* handle, uint32_t tid, char* procdirname, struct scap_ns_socket_list** sockets_by_ns, scap_threadinfo** procinfo, char *error) { char dir_name[256]; char target_name[SCAP_MAX_PATH_SIZE]; int target_res; char filename[252]; char line[SCAP_MAX_ENV_SIZE]; struct scap_threadinfo* tinfo; int32_t uth_status = SCAP_SUCCESS; FILE* f; size_t filesize; size_t exe_len; bool free_tinfo = false; int32_t res = SCAP_SUCCESS; struct stat dirstat; snprintf(dir_name, sizeof(dir_name), "%s/%u/", procdirname, tid); snprintf(filename, sizeof(filename), "%sexe", dir_name); // // Gather the executable full name // target_res = readlink(filename, target_name, sizeof(target_name) - 1); // Getting the target of the exe, i.e. to which binary it points to if(target_res <= 0) { // // No exe. This either // - a kernel thread (if there is no cmdline). In that case we skip it. // - a process that has been containerized or has some weird thing going on. In that case // we accept it. // snprintf(filename, sizeof(filename), "%scmdline", dir_name); f = fopen(filename, "r"); if(f == NULL) { return SCAP_SUCCESS; } ASSERT(sizeof(line) >= SCAP_MAX_PATH_SIZE); if(fgets(line, SCAP_MAX_PATH_SIZE, f) == NULL) { fclose(f); return SCAP_SUCCESS; } else { fclose(f); } target_name[0] = 0; } else { // null-terminate target_name (readlink() does not append a null byte) target_name[target_res] = 0; } // // This is a real user level process. Allocate the procinfo structure. // if((tinfo = scap_proc_alloc(handle)) == NULL) { // Error message saved in handle->m_lasterr snprintf(error, SCAP_LASTERR_SIZE, "can't allocate procinfo struct: %s", handle->m_lasterr); return SCAP_FAILURE; } tinfo->tid = tid; tinfo->fdlist = NULL; // // Gathers the exepath // snprintf(tinfo->exepath, sizeof(tinfo->exepath), "%s", target_name); // // Gather the command name // snprintf(filename, sizeof(filename), "%sstatus", dir_name); f = fopen(filename, "r"); if(f == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "can't open %s (error %s)", filename, scap_strerror(handle, errno)); free(tinfo); return SCAP_FAILURE; } else { ASSERT(sizeof(line) >= SCAP_MAX_PATH_SIZE); if(fgets(line, SCAP_MAX_PATH_SIZE, f) == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "can't read from %s (%s)", filename, scap_strerror(handle, errno)); fclose(f); free(tinfo); return SCAP_FAILURE; } line[SCAP_MAX_PATH_SIZE - 1] = 0; sscanf(line, "Name:%1024s", tinfo->comm); fclose(f); } bool suppressed; if ((res = scap_update_suppressed(handle, tinfo->comm, tid, 0, &suppressed)) != SCAP_SUCCESS) { snprintf(error, SCAP_LASTERR_SIZE, "can't update set of suppressed tids (%s)", handle->m_lasterr); free(tinfo); return res; } if (suppressed && !procinfo) { free(tinfo); return SCAP_SUCCESS; } // // Gather the command line // snprintf(filename, sizeof(filename), "%scmdline", dir_name); f = fopen(filename, "r"); if(f == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "can't open cmdline file %s (%s)", filename, scap_strerror(handle, errno)); free(tinfo); return SCAP_FAILURE; } else { ASSERT(sizeof(line) >= SCAP_MAX_ARGS_SIZE); filesize = fread(line, 1, SCAP_MAX_ARGS_SIZE - 1, f); if(filesize > 0) { line[filesize] = 0; exe_len = strlen(line); if(exe_len < filesize) { ++exe_len; } snprintf(tinfo->exe, SCAP_MAX_PATH_SIZE, "%s", line); tinfo->args_len = filesize - exe_len; memcpy(tinfo->args, line + exe_len, tinfo->args_len); tinfo->args[SCAP_MAX_ARGS_SIZE - 1] = 0; } else { tinfo->args[0] = 0; tinfo->exe[0] = 0; } fclose(f); } // // Gather the environment // snprintf(filename, sizeof(filename), "%senviron", dir_name); f = fopen(filename, "r"); if(f == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "can't open environ file %s (%s)", filename, scap_strerror(handle, errno)); free(tinfo); return SCAP_FAILURE; } else { ASSERT(sizeof(line) >= SCAP_MAX_ENV_SIZE); filesize = fread(line, 1, SCAP_MAX_ENV_SIZE, f); if(filesize > 0) { line[filesize - 1] = 0; tinfo->env_len = filesize; memcpy(tinfo->env, line, tinfo->env_len); tinfo->env[SCAP_MAX_ENV_SIZE - 1] = 0; } else { tinfo->env[0] = 0; } fclose(f); } // // set the current working directory of the process // if(SCAP_FAILURE == scap_proc_fill_cwd(handle, dir_name, tinfo)) { snprintf(error, SCAP_LASTERR_SIZE, "can't fill cwd for %s (%s)", dir_name, handle->m_lasterr); free(tinfo); return SCAP_FAILURE; } // // extract the user id and ppid from /proc/pid/status // if(SCAP_FAILURE == scap_proc_fill_info_from_stats(handle, dir_name, tinfo)) { snprintf(error, SCAP_LASTERR_SIZE, "can't fill cwd for %s (%s)", dir_name, handle->m_lasterr); free(tinfo); return SCAP_FAILURE; } // // Set the file limit // if(SCAP_FAILURE == scap_proc_fill_flimit(handle, tinfo->tid, tinfo)) { snprintf(error, SCAP_LASTERR_SIZE, "can't fill flimit for %s (%s)", dir_name, handle->m_lasterr); free(tinfo); return SCAP_FAILURE; } if(scap_proc_fill_cgroups(handle, tinfo, dir_name) == SCAP_FAILURE) { snprintf(error, SCAP_LASTERR_SIZE, "can't fill cgroups for %s (%s)", dir_name, handle->m_lasterr); free(tinfo); return SCAP_FAILURE; } // These values should be read already from /status file, leave these // fallback functions for older kernels < 4.1 if(tinfo->vtid == 0 && scap_get_vtid(handle, tinfo->tid, &tinfo->vtid) == SCAP_FAILURE) { tinfo->vtid = tinfo->tid; } if(tinfo->vpid == 0 && scap_get_vpid(handle, tinfo->tid, &tinfo->vpid) == SCAP_FAILURE) { tinfo->vpid = tinfo->pid; } // // set the current root of the process // if(SCAP_FAILURE == scap_proc_fill_root(handle, tinfo, dir_name)) { snprintf(error, SCAP_LASTERR_SIZE, "can't fill root for %s (%s)", dir_name, handle->m_lasterr); free(tinfo); return SCAP_FAILURE; } // // set the loginuid // if(SCAP_FAILURE == scap_proc_fill_loginuid(handle, tinfo, dir_name)) { snprintf(error, SCAP_LASTERR_SIZE, "can't fill loginuid for %s (%s)", dir_name, handle->m_lasterr); free(tinfo); return SCAP_FAILURE; } if(stat(dir_name, &dirstat) == 0) { tinfo->clone_ts = dirstat.st_ctim.tv_sec*1000000000 + dirstat.st_ctim.tv_nsec; } // If tid is different from pid, assume this is a thread and that the FDs are shared, and set the // corresponding process flags. // XXX we should see if the process creation flags are stored somewhere in /proc and handle this // properly instead of making assumptions. // if(tinfo->tid == tinfo->pid) { tinfo->flags = 0; } else { tinfo->flags = PPM_CL_CLONE_THREAD | PPM_CL_CLONE_FILES; } // // if procinfo is set we assume this is a runtime lookup so no // need to use the table // if(!procinfo) { // // Done. Add the entry to the process table, or fire the notification callback // if(handle->m_proc_callback == NULL) { HASH_ADD_INT64(handle->m_proclist, tid, tinfo); if(uth_status != SCAP_SUCCESS) { snprintf(error, SCAP_LASTERR_SIZE, "process table allocation error (2)"); return SCAP_FAILURE; } } else { handle->m_proc_callback(handle->m_proc_callback_context, handle, tinfo->tid, tinfo, NULL); free_tinfo = true; } } else { *procinfo = tinfo; } // // Only add fds for processes, not threads // if(tinfo->pid == tinfo->tid) { res = scap_fd_scan_fd_dir(handle, dir_name, tinfo, sockets_by_ns, error); } if(free_tinfo) { free(tinfo); } return res; } // // Read a single thread info from /proc // int32_t scap_proc_read_thread(scap_t* handle, char* procdirname, uint64_t tid, struct scap_threadinfo** pi, char *error, bool scan_sockets) { struct scap_ns_socket_list* sockets_by_ns = NULL; int32_t res; char add_error[SCAP_LASTERR_SIZE]; if(!scan_sockets) { sockets_by_ns = (void*)-1; } res = scap_proc_add_from_proc(handle, tid, procdirname, &sockets_by_ns, pi, add_error); if(res != SCAP_SUCCESS) { snprintf(error, SCAP_LASTERR_SIZE, "cannot add proc tid = %"PRIu64", dirname = %s, error=%s", tid, procdirname, add_error); } return res; } // // Scan a directory containing multiple processes under /proc // static int32_t _scap_proc_scan_proc_dir_impl(scap_t* handle, char* procdirname, int parenttid, char *error) { DIR *dir_p; struct dirent *dir_entry_p; scap_threadinfo* tinfo; uint64_t tid; int32_t res = SCAP_SUCCESS; char childdir[SCAP_MAX_PATH_SIZE]; struct scap_ns_socket_list* sockets_by_ns = NULL; dir_p = opendir(procdirname); if(dir_p == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "error opening the %s directory (%s)", procdirname, scap_strerror(handle, errno)); return SCAP_NOTFOUND; } while((dir_entry_p = readdir(dir_p)) != NULL) { if(strspn(dir_entry_p->d_name, "0123456789") != strlen(dir_entry_p->d_name)) { continue; } // // Gather the process TID, which is the directory name // tid = atoi(dir_entry_p->d_name); // // Skip the main thread entry // if(parenttid != -1 && tid == parenttid) { continue; } // // This is the initial /proc scan so duplicate threads // are an error, or at least unexpected. Check the process // list to see if we've encountered this tid already // HASH_FIND_INT64(handle->m_proclist, &tid, tinfo); if(tinfo != NULL) { ASSERT(false); snprintf(error, SCAP_LASTERR_SIZE, "duplicate process %"PRIu64, tid); res = SCAP_FAILURE; break; } char add_error[SCAP_LASTERR_SIZE]; // // We have a process that needs to be explored // res = scap_proc_add_from_proc(handle, tid, procdirname, &sockets_by_ns, NULL, add_error); if(res != SCAP_SUCCESS) { snprintf(error, SCAP_LASTERR_SIZE, "cannot add procs tid = %"PRIu64", parenttid = %"PRIi32", dirname = %s, error=%s", tid, parenttid, procdirname, add_error); break; } // // See if this process includes tasks that need to be added // if(parenttid == -1 && handle->m_mode != SCAP_MODE_NODRIVER) { snprintf(childdir, sizeof(childdir), "%s/%u/task", procdirname, (int)tid); if(_scap_proc_scan_proc_dir_impl(handle, childdir, tid, error) == SCAP_FAILURE) { res = SCAP_FAILURE; break; } } } closedir(dir_p); if(sockets_by_ns != NULL && sockets_by_ns != (void*)-1) { scap_fd_free_ns_sockets_list(handle, &sockets_by_ns); } return res; } int32_t scap_proc_scan_proc_dir(scap_t* handle, char* procdirname, char *error) { return _scap_proc_scan_proc_dir_impl(handle, procdirname, -1, error); } #endif // CYGWING_AGENT int32_t scap_getpid_global(scap_t* handle, int64_t* pid) { #ifndef CYGWING_AGENT if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Cannot get pid (not in live mode)"); ASSERT(false); return SCAP_FAILURE; } #if !defined(HAS_CAPTURE) ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Cannot get pid (capture not enabled)"); return SCAP_FAILURE; #else if(handle->m_bpf) { char filename[SCAP_MAX_PATH_SIZE]; char line[512]; snprintf(filename, sizeof(filename), "%s/proc/self/status", scap_get_host_root()); FILE* f = fopen(filename, "r"); if(f == NULL) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can not open status file %s (%s)", filename, scap_strerror(handle, errno)); return SCAP_FAILURE; } while(fgets(line, sizeof(line), f) != NULL) { if(sscanf(line, "Tgid: %" PRId64, pid) == 1) { fclose(f); return SCAP_SUCCESS; } } fclose(f); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "could not find tgid in status file %s", filename); return SCAP_FAILURE; } else { *pid = ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_GET_CURRENT_PID); if(*pid == -1) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "ioctl to get pid failed (%s)", scap_strerror(handle, errno)); return SCAP_FAILURE; } } return SCAP_SUCCESS; #endif #else // CYGWING_AGENT return getpid(); #endif // CYGWING_AGENT } #endif // HAS_CAPTURE #ifdef CYGWING_AGENT int32_t scap_proc_scan_proc_dir(scap_t* handle, char* procdirname, char *error) { return scap_proc_scan_proc_dir_windows(handle, error); } #endif // // Delete a process entry // void scap_proc_delete(scap_t* handle, scap_threadinfo* proc) { // // First, free the fd table for this process descriptor // scap_fd_free_proc_fd_table(handle, proc); // // Second, remove the process descriptor from the table // HASH_DEL(handle->m_proclist, proc); // // Third, free the memory // free(proc); } // // Free the process table // void scap_proc_free_table(scap_t* handle) { struct scap_threadinfo* tinfo; struct scap_threadinfo* ttinfo; HASH_ITER(hh, handle->m_proclist, tinfo, ttinfo) { scap_proc_delete(handle, tinfo); } } struct scap_threadinfo* scap_proc_get(scap_t* handle, int64_t tid, bool scan_sockets) { #if !defined(HAS_CAPTURE) return NULL; #else // // No /proc parsing for offline captures // if(handle->m_mode == SCAP_MODE_CAPTURE) { return NULL; } struct scap_threadinfo* tinfo = NULL; char filename[SCAP_MAX_PATH_SIZE]; snprintf(filename, sizeof(filename), "%s/proc", scap_get_host_root()); if(scap_proc_read_thread(handle, filename, tid, &tinfo, handle->m_lasterr, scan_sockets) != SCAP_SUCCESS) { return NULL; } return tinfo; #endif // HAS_CAPTURE } bool scap_is_thread_alive(scap_t* handle, int64_t pid, int64_t tid, const char* comm) { #if !defined(HAS_CAPTURE) return false; #else char charbuf[SCAP_MAX_PATH_SIZE]; FILE* f; // // No /proc parsing for offline captures // if(handle->m_mode == SCAP_MODE_CAPTURE) { return false; } snprintf(charbuf, sizeof(charbuf), "%s/proc/%" PRId64 "/task/%" PRId64 "/comm", scap_get_host_root(), pid, tid); f = fopen(charbuf, "r"); if(f != NULL) { if(fgets(charbuf, sizeof(charbuf), f) != NULL) { if(strncmp(charbuf, comm, strlen(comm)) == 0) { fclose(f); return true; } } fclose(f); } else { // // If /proc//task//comm does not exist but /proc//task//exe does exist, we assume we're on an ancient // OS like RHEL5 and we return true. // This could generate some false positives on such old distros, and we're going to accept it. // snprintf(charbuf, sizeof(charbuf), "%s/proc/%" PRId64 "/task/%" PRId64 "/exe", scap_get_host_root(), pid, tid); f = fopen(charbuf, "r"); if(f != NULL) { fclose(f); return true; } } return false; #endif // HAS_CAPTURE } #if defined(HAS_CAPTURE) int scap_proc_scan_proc_table(scap_t *handle) { char filename[SCAP_MAX_PATH_SIZE]; // // Create the process list // handle->m_lasterr[0] = '\0'; snprintf(filename, sizeof(filename), "%s/proc", scap_get_host_root()); return scap_proc_scan_proc_dir(handle, filename, handle->m_lasterr); } void scap_refresh_proc_table(scap_t* handle) { if(handle->m_proclist) { scap_proc_free_table(handle); handle->m_proclist = NULL; } scap_proc_scan_proc_table(handle); } #else void scap_refresh_proc_table(scap_t* handle) { } #endif // HAS_CAPTURE struct scap_threadinfo *scap_proc_alloc(scap_t *handle) { struct scap_threadinfo *tinfo = (struct scap_threadinfo*) calloc(1, sizeof(scap_threadinfo)); if(tinfo == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "process table allocation error (1)"); return NULL; } return tinfo; } void scap_proc_free(scap_t* handle, struct scap_threadinfo* proc) { scap_fd_free_proc_fd_table(handle, proc); free(proc); } int32_t scap_proc_add(scap_t* handle, uint64_t tid, scap_threadinfo* tinfo) { int32_t uth_status = SCAP_SUCCESS; HASH_ADD_INT64(handle->m_proclist, tid, tinfo); if(uth_status == SCAP_SUCCESS) { return SCAP_SUCCESS; } else { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not add tid to hash table"); return SCAP_FAILURE; } } int32_t scap_fd_add(scap_t *handle, scap_threadinfo* tinfo, uint64_t fd, scap_fdinfo* fdinfo) { int32_t uth_status = SCAP_SUCCESS; HASH_ADD_INT64(tinfo->fdlist, fd, fdinfo); if(uth_status == SCAP_SUCCESS) { return SCAP_SUCCESS; } else { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not add fd to hash table"); return SCAP_FAILURE; } } // // Internal helper functions to output the process table to screen // void scap_proc_print_info(scap_t *handle, scap_threadinfo* tinfo) { fprintf(stderr, "TID:%"PRIu64" PID:%"PRIu64" FLAGS:%"PRIu32" COMM:%s EXE:%s ARGS:%s CWD:%s FLIMIT:%" PRId64 "\n", tinfo->tid, tinfo->pid, tinfo->flags,tinfo->comm, tinfo->exe, tinfo->args, tinfo->cwd, tinfo->fdlimit); scap_fd_print_table(handle, tinfo); } void scap_proc_print_proc_by_tid(scap_t* handle, uint64_t tid) { scap_threadinfo* tinfo; scap_threadinfo* ttinfo; HASH_ITER(hh, handle->m_proclist, tinfo, ttinfo) { if(tinfo->tid == tid) { scap_proc_print_info(handle, tinfo); } } } void scap_proc_print_table(scap_t* handle) { scap_threadinfo* tinfo; scap_threadinfo* ttinfo; printf("************** PROCESS TABLE **************\n"); HASH_ITER(hh, handle->m_proclist, tinfo, ttinfo) { scap_proc_print_info(handle, tinfo); } printf("*******************************************\n"); } const char *scap_strerror(scap_t *handle, int errnum) { int rc; if((rc = strerror_r(errnum, handle->m_strerror_buf, SCAP_LASTERR_SIZE) != 0)) { if(rc != ERANGE) { snprintf(handle->m_strerror_buf, SCAP_LASTERR_SIZE, "Errno %d", errnum); } } return handle->m_strerror_buf; } int32_t scap_update_suppressed(scap_t *handle, const char *comm, uint64_t tid, uint64_t ptid, bool *suppressed) { uint32_t i; scap_tid *stid; *suppressed = false; HASH_FIND_INT64(handle->m_suppressed_tids, &ptid, stid); if(stid != NULL) { *suppressed = true; } else { for(i=0; i < handle->m_num_suppressed_comms; i++) { if(strcmp(handle->m_suppressed_comms[i], comm) == 0) { *suppressed = true; break; } } } // Also check to see if the tid is already in the set of // suppressed tids. HASH_FIND_INT64(handle->m_suppressed_tids, &tid, stid); if(*suppressed && stid == NULL) { stid = (scap_tid *) malloc(sizeof(scap_tid)); stid->tid = tid; int32_t uth_status = SCAP_SUCCESS; HASH_ADD_INT64(handle->m_suppressed_tids, tid, stid); if(uth_status != SCAP_SUCCESS) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can't add tid to suppressed hash table"); return SCAP_FAILURE; } *suppressed = true; } else if (!*suppressed && stid != NULL) { HASH_DEL(handle->m_suppressed_tids, stid); free(stid); *suppressed = false; } return SCAP_SUCCESS; } int32_t scap_check_suppressed(scap_t *handle, scap_evt *pevent, bool *suppressed) { uint16_t *lens; char *valptr; uint32_t j; int32_t res = SCAP_SUCCESS; const char *comm = NULL; uint64_t *ptid = NULL; scap_tid *stid; *suppressed = false; // For events that can create a new tid (fork, vfork, clone), // we need to check the comm, which might also update the set // of suppressed tids. switch(pevent->type) { case PPME_SYSCALL_CLONE_20_X: case PPME_SYSCALL_FORK_20_X: case PPME_SYSCALL_VFORK_20_X: case PPME_SYSCALL_EXECVE_19_X: lens = (uint16_t *)((char *)pevent + sizeof(struct ppm_evt_hdr)); valptr = (char *)lens + pevent->nparams * sizeof(uint16_t); if(pevent->nparams < 14) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not find process comm in event argument list"); return SCAP_FAILURE; } // For all of these events, the comm is argument 14, // so we need to walk the list of params that far to // find the comm. for(j = 0; j < 13; j++) { if(j == 5) { ptid = (uint64_t *) valptr; } valptr += lens[j]; } if(ptid == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "Could not find ptid in event argument list"); return SCAP_FAILURE; } comm = valptr; if((res = scap_update_suppressed(handle, comm, pevent->tid, *ptid, suppressed)) != SCAP_SUCCESS) { // scap_update_suppressed already set handle->m_lasterr on error. return res; } break; default: HASH_FIND_INT64(handle->m_suppressed_tids, &(pevent->tid), stid); // When threads exit they are always removed and no longer suppressed. if(pevent->type == PPME_PROCEXIT_1_E) { if(stid != NULL) { HASH_DEL(handle->m_suppressed_tids, stid); free(stid); *suppressed = true; } else { *suppressed = false; } } else { *suppressed = (stid != NULL); } break; } return SCAP_SUCCESS; } sysdig-0.26.4/userspace/libscap/scap_savefile.c000077500000000000000000002227171352731327100215060ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #ifndef _WIN32 #include #include #else struct iovec { void *iov_base; /* Starting address */ size_t iov_len; /* Number of bytes to transfer */ }; #endif #include "scap.h" #include "scap-int.h" #include "scap_savefile.h" /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // WRITE FUNCTIONS /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // Write data into a dump file // int scap_dump_write(scap_dumper_t *d, void* buf, unsigned len) { if(d->m_type == DT_FILE) { return gzwrite(d->m_f, buf, len); } else { if(d->m_targetbufcurpos + len < d->m_targetbufend) { memcpy(d->m_targetbufcurpos, buf, len); d->m_targetbufcurpos += len; return len; } else { return -1; } } } int scap_dump_writev(scap_dumper_t *d, const struct iovec *iov, int iovcnt) { unsigned totlen = 0; int i; for (i = 0; i < iovcnt; i++) { if(scap_dump_write(d, iov[i].iov_base, iov[i].iov_len) < 0) { return -1; } totlen += iov[i].iov_len; } return totlen; } int32_t compr(uint8_t* dest, uint64_t* destlen, const uint8_t* source, uint64_t sourcelen, int level) { uLongf dl = compressBound(sourcelen); if(dl >= *destlen) { return SCAP_FAILURE; } int res = compress2(dest, &dl, source, sourcelen, level); if(res == Z_OK) { *destlen = (uint64_t)dl; return SCAP_SUCCESS; } else { return SCAP_FAILURE; } } uint8_t* scap_get_memorydumper_curpos(scap_dumper_t *d) { return d->m_targetbufcurpos; } #ifndef _WIN32 static inline uint32_t scap_normalize_block_len(uint32_t blocklen) #else static uint32_t scap_normalize_block_len(uint32_t blocklen) #endif { return ((blocklen + 3) >> 2) << 2; } static int32_t scap_write_padding(scap_dumper_t *d, uint32_t blocklen) { int32_t val = 0; uint32_t bytestowrite = scap_normalize_block_len(blocklen) - blocklen; if(scap_dump_write(d, &val, bytestowrite) == bytestowrite) { return SCAP_SUCCESS; } else { return SCAP_FAILURE; } } int32_t scap_write_proc_fds(scap_t *handle, struct scap_threadinfo *tinfo, scap_dumper_t *d) { block_header bh; uint32_t bt; uint32_t totlen = MEMBER_SIZE(scap_threadinfo, tid); // This includes the tid uint32_t idx = 0; struct scap_fdinfo *fdi; struct scap_fdinfo *tfdi; uint32_t* lengths = calloc(HASH_COUNT(tinfo->fdlist), sizeof(uint32_t)); if(lengths == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_write_proc_fds memory allocation failure"); return SCAP_FAILURE; } // // First pass of the table to calculate the lengths // HASH_ITER(hh, tinfo->fdlist, fdi, tfdi) { if(fdi->type != SCAP_FD_UNINITIALIZED && fdi->type != SCAP_FD_UNKNOWN) { uint32_t fl = scap_fd_info_len(fdi); lengths[idx++] = fl; totlen += fl; } } idx = 0; // // Create the block // bh.block_type = FDL_BLOCK_TYPE_V2; bh.block_total_length = scap_normalize_block_len(sizeof(block_header) + totlen + 4); if(scap_dump_write(d, &bh, sizeof(bh)) != sizeof(bh)) { free(lengths); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fd1)"); return SCAP_FAILURE; } // // Write the tid // if(scap_dump_write(d, &tinfo->tid, sizeof(tinfo->tid)) != sizeof(tinfo->tid)) { free(lengths); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fd2)"); return SCAP_FAILURE; } // // Second pass of the table to dump it // HASH_ITER(hh, tinfo->fdlist, fdi, tfdi) { if(fdi->type != SCAP_FD_UNINITIALIZED && fdi->type != SCAP_FD_UNKNOWN) { if(scap_fd_write_to_disk(handle, fdi, d, lengths[idx++]) != SCAP_SUCCESS) { free(lengths); return SCAP_FAILURE; } } } free(lengths); // // Add the padding // if(scap_write_padding(d, totlen) != SCAP_SUCCESS) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fd3)"); return SCAP_FAILURE; } // // Create the trailer // bt = bh.block_total_length; if(scap_dump_write(d, &bt, sizeof(bt)) != sizeof(bt)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fd4)"); return SCAP_FAILURE; } return SCAP_SUCCESS; } // // Write the fd list blocks // static int32_t scap_write_fdlist(scap_t *handle, scap_dumper_t *d) { struct scap_threadinfo *tinfo; struct scap_threadinfo *ttinfo; int32_t res; HASH_ITER(hh, handle->m_proclist, tinfo, ttinfo) { if(!tinfo->filtered_out) { res = scap_write_proc_fds(handle, tinfo, d); if(res != SCAP_SUCCESS) { return res; } } } return SCAP_SUCCESS; } // // Write the process list block // int32_t scap_write_proclist_header(scap_t *handle, scap_dumper_t *d, uint32_t totlen) { block_header bh; // // Create the block header // bh.block_type = PL_BLOCK_TYPE_V9; bh.block_total_length = scap_normalize_block_len(sizeof(block_header) + totlen + 4); if(scap_dump_write(d, &bh, sizeof(bh)) != sizeof(bh)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (1)"); return SCAP_FAILURE; } return SCAP_SUCCESS; } // // Write the process list block // int32_t scap_write_proclist_trailer(scap_t *handle, scap_dumper_t *d, uint32_t totlen) { block_header bh; uint32_t bt; bh.block_type = PL_BLOCK_TYPE_V9; bh.block_total_length = scap_normalize_block_len(sizeof(block_header) + totlen + 4); // // Blocks need to be 4-byte padded // if(scap_write_padding(d, totlen) != SCAP_SUCCESS) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (3)"); return SCAP_FAILURE; } // // Create the trailer // bt = bh.block_total_length; if(scap_dump_write(d, &bt, sizeof(bt)) != sizeof(bt)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (4)"); return SCAP_FAILURE; } return SCAP_SUCCESS; } // // Write the process list block // int32_t scap_write_proclist_entry(scap_t *handle, scap_dumper_t *d, struct scap_threadinfo *tinfo, uint32_t len) { struct iovec args = {tinfo->args, tinfo->args_len}; struct iovec env = {tinfo->env, tinfo->env_len}; struct iovec cgroups = {tinfo->cgroups, tinfo->cgroups_len}; return scap_write_proclist_entry_bufs(handle, d, tinfo, len, tinfo->comm, tinfo->exe, tinfo->exepath, &args, 1, &env, 1, tinfo->cwd, &cgroups, 1, tinfo->root); } static uint16_t iov_size(const struct iovec *iov, uint32_t iovcnt) { uint16_t len = 0; uint32_t i; for (i = 0; i < iovcnt; i++) { len += iov[i].iov_len; } return len; } int32_t scap_write_proclist_entry_bufs(scap_t *handle, scap_dumper_t *d, struct scap_threadinfo *tinfo, uint32_t len, const char *comm, const char *exe, const char *exepath, const struct iovec *args, int argscnt, const struct iovec *envs, int envscnt, const char *cwd, const struct iovec *cgroups, int cgroupscnt, const char *root) { uint16_t commlen; uint16_t exelen; uint16_t exepathlen; uint16_t cwdlen; uint16_t rootlen; uint16_t argslen; uint16_t envlen; uint16_t cgroupslen; commlen = (uint16_t)strnlen(comm, SCAP_MAX_PATH_SIZE); exelen = (uint16_t)strnlen(exe, SCAP_MAX_PATH_SIZE); exepathlen = (uint16_t)strnlen(exepath, SCAP_MAX_PATH_SIZE); cwdlen = (uint16_t)strnlen(cwd, SCAP_MAX_PATH_SIZE); rootlen = (uint16_t)strnlen(root, SCAP_MAX_PATH_SIZE); argslen = iov_size(args, argscnt); envlen = iov_size(envs, envscnt); cgroupslen = iov_size(cgroups, cgroupscnt); if(scap_dump_write(d, &len, sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(tinfo->tid), sizeof(uint64_t)) != sizeof(uint64_t) || scap_dump_write(d, &(tinfo->pid), sizeof(uint64_t)) != sizeof(uint64_t) || scap_dump_write(d, &(tinfo->ptid), sizeof(uint64_t)) != sizeof(uint64_t) || scap_dump_write(d, &(tinfo->sid), sizeof(uint64_t)) != sizeof(uint64_t) || scap_dump_write(d, &(tinfo->vpgid), sizeof(uint64_t)) != sizeof(uint64_t) || scap_dump_write(d, &commlen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, (char *) comm, commlen) != commlen || scap_dump_write(d, &exelen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, (char *) exe, exelen) != exelen || scap_dump_write(d, &exepathlen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, (char *) exepath, exepathlen) != exepathlen || scap_dump_write(d, &argslen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_writev(d, args, argscnt) != argslen || scap_dump_write(d, &cwdlen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, (char *) cwd, cwdlen) != cwdlen || scap_dump_write(d, &(tinfo->fdlimit), sizeof(uint64_t)) != sizeof(uint64_t) || scap_dump_write(d, &(tinfo->flags), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(tinfo->uid), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(tinfo->gid), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(tinfo->vmsize_kb), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(tinfo->vmrss_kb), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(tinfo->vmswap_kb), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(tinfo->pfmajor), sizeof(uint64_t)) != sizeof(uint64_t) || scap_dump_write(d, &(tinfo->pfminor), sizeof(uint64_t)) != sizeof(uint64_t) || scap_dump_write(d, &envlen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_writev(d, envs, envscnt) != envlen || scap_dump_write(d, &(tinfo->vtid), sizeof(int64_t)) != sizeof(int64_t) || scap_dump_write(d, &(tinfo->vpid), sizeof(int64_t)) != sizeof(int64_t) || scap_dump_write(d, &(cgroupslen), sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_writev(d, cgroups, cgroupscnt) != cgroupslen || scap_dump_write(d, &rootlen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, (char *) root, rootlen) != rootlen || scap_dump_write(d, &(tinfo->loginuid), sizeof(uint32_t)) != sizeof(uint32_t)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (2)"); return SCAP_FAILURE; } return SCAP_SUCCESS; } // // Write the process list block // static int32_t scap_write_proclist(scap_t *handle, scap_dumper_t *d) { uint32_t totlen = 0; uint32_t idx = 0; struct scap_threadinfo *tinfo; struct scap_threadinfo *ttinfo; uint32_t* lengths = calloc(HASH_COUNT(handle->m_proclist), sizeof(uint32_t)); if(lengths == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_write_proclist memory allocation failure"); return SCAP_FAILURE; } // // First pass of the table to calculate the lengths // HASH_ITER(hh, handle->m_proclist, tinfo, ttinfo) { if(!tinfo->filtered_out) { // // NB: new fields must be appended // uint32_t il= (uint32_t) (sizeof(uint32_t) + // len sizeof(uint64_t) + // tid sizeof(uint64_t) + // pid sizeof(uint64_t) + // ptid sizeof(uint64_t) + // sid sizeof(uint64_t) + // vpgid 2 + strnlen(tinfo->comm, SCAP_MAX_PATH_SIZE) + 2 + strnlen(tinfo->exe, SCAP_MAX_PATH_SIZE) + 2 + strnlen(tinfo->exepath, SCAP_MAX_PATH_SIZE) + 2 + tinfo->args_len + 2 + strnlen(tinfo->cwd, SCAP_MAX_PATH_SIZE) + sizeof(uint64_t) + // fdlimit sizeof(uint32_t) + // flags sizeof(uint32_t) + // uid sizeof(uint32_t) + // gid sizeof(uint32_t) + // vmsize_kb sizeof(uint32_t) + // vmrss_kb sizeof(uint32_t) + // vmswap_kb sizeof(uint64_t) + // pfmajor sizeof(uint64_t) + // pfminor 2 + tinfo->env_len + sizeof(int64_t) + // vtid sizeof(int64_t) + // vpid 2 + tinfo->cgroups_len + 2 + strnlen(tinfo->root, SCAP_MAX_PATH_SIZE) + sizeof(int32_t)); // loginuid; lengths[idx++] = il; totlen += il; } } idx = 0; if(scap_write_proclist_header(handle, d, totlen) != SCAP_SUCCESS) { free(lengths); return SCAP_FAILURE; } // // Second pass of the table to dump it // HASH_ITER(hh, handle->m_proclist, tinfo, ttinfo) { if(tinfo->filtered_out) { continue; } if(scap_write_proclist_entry(handle, d, tinfo, lengths[idx++]) != SCAP_SUCCESS) { free(lengths); return SCAP_FAILURE; } } free(lengths); return scap_write_proclist_trailer(handle, d, totlen); } // // Write the machine info block // static int32_t scap_write_machine_info(scap_t *handle, scap_dumper_t *d) { block_header bh; uint32_t bt; // // Write the section header // bh.block_type = MI_BLOCK_TYPE; bh.block_total_length = scap_normalize_block_len(sizeof(block_header) + sizeof(scap_machine_info) + 4); bt = bh.block_total_length; if(scap_dump_write(d, &bh, sizeof(bh)) != sizeof(bh) || scap_dump_write(d, &handle->m_machine_info, sizeof(handle->m_machine_info)) != sizeof(handle->m_machine_info) || scap_dump_write(d, &bt, sizeof(bt)) != sizeof(bt)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (MI1)"); return SCAP_FAILURE; } return SCAP_SUCCESS; } // // Write the interface list block // static int32_t scap_write_iflist(scap_t *handle, scap_dumper_t* d) { block_header bh; uint32_t bt; uint32_t entrylen; uint32_t totlen = 0; uint32_t j; // // Get the interface list // if(handle->m_addrlist == NULL) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to trace file: interface list missing"); return SCAP_FAILURE; } // // Create the block // bh.block_type = IL_BLOCK_TYPE_V2; bh.block_total_length = scap_normalize_block_len(sizeof(block_header) + (handle->m_addrlist->n_v4_addrs + handle->m_addrlist->n_v6_addrs)*sizeof(uint32_t) + handle->m_addrlist->totlen + 4); if(scap_dump_write(d, &bh, sizeof(bh)) != sizeof(bh)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (IF1)"); return SCAP_FAILURE; } // // Dump the ipv4 list // for(j = 0; j < handle->m_addrlist->n_v4_addrs; j++) { scap_ifinfo_ipv4 *entry = &(handle->m_addrlist->v4list[j]); entrylen = sizeof(scap_ifinfo_ipv4) + entry->ifnamelen - SCAP_MAX_PATH_SIZE; if(scap_dump_write(d, &entrylen, sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(entry->type), sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, &(entry->ifnamelen), sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, &(entry->addr), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(entry->netmask), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(entry->bcast), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(entry->linkspeed), sizeof(uint64_t)) != sizeof(uint64_t) || scap_dump_write(d, &(entry->ifname), entry->ifnamelen) != entry->ifnamelen) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (IF2)"); return SCAP_FAILURE; } totlen += sizeof(uint32_t) + entrylen; } // // Dump the ipv6 list // for(j = 0; j < handle->m_addrlist->n_v6_addrs; j++) { scap_ifinfo_ipv6 *entry = &(handle->m_addrlist->v6list[j]); entrylen = sizeof(scap_ifinfo_ipv6) + entry->ifnamelen - SCAP_MAX_PATH_SIZE; if(scap_dump_write(d, &entrylen, sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(entry->type), sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, &(entry->ifnamelen), sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, &(entry->addr), SCAP_IPV6_ADDR_LEN) != SCAP_IPV6_ADDR_LEN || scap_dump_write(d, &(entry->netmask), SCAP_IPV6_ADDR_LEN) != SCAP_IPV6_ADDR_LEN || scap_dump_write(d, &(entry->bcast), SCAP_IPV6_ADDR_LEN) != SCAP_IPV6_ADDR_LEN || scap_dump_write(d, &(entry->linkspeed), sizeof(uint64_t)) != sizeof(uint64_t) || scap_dump_write(d, &(entry->ifname), entry->ifnamelen) != entry->ifnamelen) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (IF2)"); return SCAP_FAILURE; } totlen += sizeof(uint32_t) + entrylen; } // // Blocks need to be 4-byte padded // if(scap_write_padding(d, totlen) != SCAP_SUCCESS) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (IF3)"); return SCAP_FAILURE; } // // Create the trailer // bt = bh.block_total_length; if(scap_dump_write(d, &bt, sizeof(bt)) != sizeof(bt)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (IF4)"); return SCAP_FAILURE; } return SCAP_SUCCESS; } // // Write the user list block // static int32_t scap_write_userlist(scap_t *handle, scap_dumper_t* d) { block_header bh; uint32_t bt; uint32_t j; uint16_t namelen; uint16_t homedirlen; uint16_t shelllen; uint8_t type; uint32_t totlen = 0; // // Make sure we have a user list interface list // if(handle->m_userlist == NULL) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to trace file: user list missing"); return SCAP_FAILURE; } uint32_t* lengths = calloc(handle->m_userlist->nusers + handle->m_userlist->ngroups, sizeof(uint32_t)); if(lengths == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_write_userlist memory allocation failure (1)"); return SCAP_FAILURE; } // // Calculate the lengths // for(j = 0; j < handle->m_userlist->nusers; j++) { scap_userinfo* info = &handle->m_userlist->users[j]; namelen = (uint16_t)strnlen(info->name, MAX_CREDENTIALS_STR_LEN); homedirlen = (uint16_t)strnlen(info->homedir, SCAP_MAX_PATH_SIZE); shelllen = (uint16_t)strnlen(info->shell, SCAP_MAX_PATH_SIZE); // NB: new fields must be appended size_t ul = sizeof(uint32_t) + sizeof(type) + sizeof(info->uid) + sizeof(info->gid) + sizeof(uint16_t) + namelen + sizeof(uint16_t) + homedirlen + sizeof(uint16_t) + shelllen; totlen += ul; lengths[j] = ul; } for(j = 0; j < handle->m_userlist->ngroups; j++) { scap_groupinfo* info = &handle->m_userlist->groups[j]; namelen = (uint16_t)strnlen(info->name, MAX_CREDENTIALS_STR_LEN); // NB: new fields must be appended uint32_t gl = sizeof(uint32_t) + sizeof(type) + sizeof(info->gid) + sizeof(uint16_t) + namelen; totlen += gl; lengths[handle->m_userlist->nusers + j] = gl; } // // Create the block // bh.block_type = UL_BLOCK_TYPE_V2; bh.block_total_length = scap_normalize_block_len(sizeof(block_header) + totlen + 4); if(scap_dump_write(d, &bh, sizeof(bh)) != sizeof(bh)) { free(lengths); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (IF1)"); return SCAP_FAILURE; } // // Dump the users // type = USERBLOCK_TYPE_USER; for(j = 0; j < handle->m_userlist->nusers; j++) { scap_userinfo* info = &handle->m_userlist->users[j]; namelen = (uint16_t)strnlen(info->name, MAX_CREDENTIALS_STR_LEN); homedirlen = (uint16_t)strnlen(info->homedir, SCAP_MAX_PATH_SIZE); shelllen = (uint16_t)strnlen(info->shell, SCAP_MAX_PATH_SIZE); if(scap_dump_write(d, &(lengths[j]), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(type), sizeof(type)) != sizeof(type) || scap_dump_write(d, &(info->uid), sizeof(info->uid)) != sizeof(info->uid) || scap_dump_write(d, &(info->gid), sizeof(info->gid)) != sizeof(info->gid) || scap_dump_write(d, &namelen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, info->name, namelen) != namelen || scap_dump_write(d, &homedirlen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, info->homedir, homedirlen) != homedirlen || scap_dump_write(d, &shelllen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, info->shell, shelllen) != shelllen) { free(lengths); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (U1)"); return SCAP_FAILURE; } } // // Dump the groups // type = USERBLOCK_TYPE_GROUP; for(j = 0; j < handle->m_userlist->ngroups; j++) { scap_groupinfo* info = &handle->m_userlist->groups[j]; namelen = (uint16_t)strnlen(info->name, MAX_CREDENTIALS_STR_LEN); if(scap_dump_write(d, &(lengths[handle->m_userlist->nusers + j]), sizeof(uint32_t)) != sizeof(uint32_t) || scap_dump_write(d, &(type), sizeof(type)) != sizeof(type) || scap_dump_write(d, &(info->gid), sizeof(info->gid)) != sizeof(info->gid) || scap_dump_write(d, &namelen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, info->name, namelen) != namelen) { free(lengths); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (U2)"); return SCAP_FAILURE; } } free(lengths); // // Blocks need to be 4-byte padded // if(scap_write_padding(d, totlen) != SCAP_SUCCESS) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (IF3)"); return SCAP_FAILURE; } // // Create the trailer // bt = bh.block_total_length; if(scap_dump_write(d, &bt, sizeof(bt)) != sizeof(bt)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (IF4)"); return SCAP_FAILURE; } return SCAP_SUCCESS; } // // Create the dump file headers and add the tables // int32_t scap_setup_dump(scap_t *handle, scap_dumper_t* d, const char *fname) { block_header bh; section_header_block sh; uint32_t bt; // // Write the section header // bh.block_type = SHB_BLOCK_TYPE; bh.block_total_length = sizeof(block_header) + sizeof(section_header_block) + 4; sh.byte_order_magic = SHB_MAGIC; sh.major_version = CURRENT_MAJOR_VERSION; sh.minor_version = CURRENT_MINOR_VERSION; sh.section_length = 0xffffffffffffffffLL; bt = bh.block_total_length; if(scap_dump_write(d, &bh, sizeof(bh)) != sizeof(bh) || scap_dump_write(d, &sh, sizeof(sh)) != sizeof(sh) || scap_dump_write(d, &bt, sizeof(bt)) != sizeof(bt)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file %s (5)", fname); return SCAP_FAILURE; } // // If we're dumping in live mode, refresh the process tables list // so we don't lose information about processes created in the interval // between opening the handle and starting the dump // #if defined(HAS_CAPTURE) if(handle->m_file == NULL && handle->refresh_proc_table_when_saving) { proc_entry_callback tcb = handle->m_proc_callback; handle->m_proc_callback = NULL; scap_proc_free_table(handle); char filename[SCAP_MAX_PATH_SIZE]; snprintf(filename, sizeof(filename), "%s/proc", scap_get_host_root()); if(scap_proc_scan_proc_dir(handle, filename, handle->m_lasterr) != SCAP_SUCCESS) { handle->m_proc_callback = tcb; return SCAP_FAILURE; } handle->m_proc_callback = tcb; } #endif // // Write the machine info // if(scap_write_machine_info(handle, d) != SCAP_SUCCESS) { return SCAP_FAILURE; } // // Write the interface list // if(scap_write_iflist(handle, d) != SCAP_SUCCESS) { return SCAP_FAILURE; } // // Write the user list // if(scap_write_userlist(handle, d) != SCAP_SUCCESS) { return SCAP_FAILURE; } // // Write the process list // if(scap_write_proclist(handle, d) != SCAP_SUCCESS) { return SCAP_FAILURE; } // // Write the fd lists // if(scap_write_fdlist(handle, d) != SCAP_SUCCESS) { return SCAP_FAILURE; } // // If the user doesn't need the thread table, free it // if(handle->m_proc_callback != NULL) { scap_proc_free_table(handle); } // // Done, return the file // return SCAP_SUCCESS; } // fname is only used for log messages in scap_setup_dump static scap_dumper_t *scap_dump_open_gzfile(scap_t *handle, gzFile gzfile, const char *fname, bool skip_proc_scan) { scap_dumper_t* res = (scap_dumper_t*)malloc(sizeof(scap_dumper_t)); res->m_f = gzfile; res->m_type = DT_FILE; res->m_targetbuf = NULL; res->m_targetbufcurpos = NULL; res->m_targetbufend = NULL; bool tmp_refresh_proc_table_when_saving = handle->refresh_proc_table_when_saving; if(skip_proc_scan) { handle->refresh_proc_table_when_saving = false; } if(scap_setup_dump(handle, res, fname) != SCAP_SUCCESS) { res = NULL; } if(skip_proc_scan) { handle->refresh_proc_table_when_saving = tmp_refresh_proc_table_when_saving; } return res; } // // Open a "savefile" for writing. // scap_dumper_t *scap_dump_open(scap_t *handle, const char *fname, compression_mode compress, bool skip_proc_scan) { gzFile f = NULL; int fd = -1; const char* mode; switch(compress) { case SCAP_COMPRESSION_GZIP: mode = "wb"; break; case SCAP_COMPRESSION_NONE: mode = "wbT"; break; default: ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid compression mode"); return NULL; } if(fname[0] == '-' && fname[1] == '\0') { #ifndef _WIN32 fd = dup(STDOUT_FILENO); #else fd = 1; #endif if(fd != -1) { f = gzdopen(fd, mode); fname = "standard output"; } } else { f = gzopen(fname, mode); } if(f == NULL) { #ifndef _WIN32 if(fd != -1) { close(fd); } #endif snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can't open %s", fname); return NULL; } return scap_dump_open_gzfile(handle, f, fname, skip_proc_scan); } // // Open a savefile for writing, using the provided fd scap_dumper_t* scap_dump_open_fd(scap_t *handle, int fd, compression_mode compress, bool skip_proc_scan) { gzFile f = NULL; const char* mode; switch(compress) { case SCAP_COMPRESSION_GZIP: mode = "wb"; break; case SCAP_COMPRESSION_NONE: mode = "wbT"; break; default: ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid compression mode"); return NULL; } f = gzdopen(fd, mode); if(f == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "can't open fd %d", fd); return NULL; } return scap_dump_open_gzfile(handle, f, "", skip_proc_scan); } // // Open a memory "savefile" // scap_dumper_t *scap_memory_dump_open(scap_t *handle, uint8_t* targetbuf, uint64_t targetbufsize) { scap_dumper_t* res = (scap_dumper_t*)malloc(sizeof(scap_dumper_t)); if(res == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_dump_memory_open memory allocation failure (1)"); return NULL; } res->m_f = NULL; res->m_type = DT_MEM; res->m_targetbuf = targetbuf; res->m_targetbufcurpos = targetbuf; res->m_targetbufend = targetbuf + targetbufsize; // // Disable proc parsing since it would be too heavy when saving to memory. // Before doing that, backup handle->refresh_proc_table_when_saving so we can // restore whatever the current seetting is as soon as we're done. // bool tmp_refresh_proc_table_when_saving = handle->refresh_proc_table_when_saving; handle->refresh_proc_table_when_saving = false; if(scap_setup_dump(handle, res, "") != SCAP_SUCCESS) { free(res); res = NULL; } handle->refresh_proc_table_when_saving = tmp_refresh_proc_table_when_saving; return res; } // // Close a "savefile" opened with scap_dump_open // void scap_dump_close(scap_dumper_t *d) { if(d->m_type == DT_FILE) { gzclose(d->m_f); } free(d); } // // Return the current size of a tracefile // int64_t scap_dump_get_offset(scap_dumper_t *d) { if(d->m_type == DT_FILE) { return gzoffset(d->m_f); } else { return (int64_t)d->m_targetbufcurpos - (int64_t)d->m_targetbuf; } } int64_t scap_dump_ftell(scap_dumper_t *d) { if(d->m_type == DT_FILE) { return gztell(d->m_f); } else { return (int64_t)d->m_targetbufcurpos - (int64_t)d->m_targetbuf; } } void scap_dump_flush(scap_dumper_t *d) { if(d->m_type == DT_FILE) { gzflush(d->m_f, Z_FULL_FLUSH); } } // // Tell me how many bytes we will have written if we did. // int32_t scap_number_of_bytes_to_write(scap_evt *e, uint16_t cpuid, int32_t *bytes) { *bytes = scap_normalize_block_len(sizeof(block_header) + sizeof(cpuid) + e->len + 4); return SCAP_SUCCESS; } // // Write an event to a dump file // int32_t scap_dump(scap_t *handle, scap_dumper_t *d, scap_evt *e, uint16_t cpuid, uint32_t flags) { block_header bh; uint32_t bt; if(flags == 0) { // // Write the section header // bh.block_type = EV_BLOCK_TYPE_V2; bh.block_total_length = scap_normalize_block_len(sizeof(block_header) + sizeof(cpuid) + e->len + 4); bt = bh.block_total_length; if(scap_dump_write(d, &bh, sizeof(bh)) != sizeof(bh) || scap_dump_write(d, &cpuid, sizeof(cpuid)) != sizeof(cpuid) || scap_dump_write(d, e, e->len) != e->len || scap_write_padding(d, sizeof(cpuid) + e->len) != SCAP_SUCCESS || scap_dump_write(d, &bt, sizeof(bt)) != sizeof(bt)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (6)"); return SCAP_FAILURE; } } else { // // Write the section header // bh.block_type = EVF_BLOCK_TYPE_V2; bh.block_total_length = scap_normalize_block_len(sizeof(block_header) + sizeof(cpuid) + sizeof(flags) + e->len + 4); bt = bh.block_total_length; if(scap_dump_write(d, &bh, sizeof(bh)) != sizeof(bh) || scap_dump_write(d, &cpuid, sizeof(cpuid)) != sizeof(cpuid) || scap_dump_write(d, &flags, sizeof(flags)) != sizeof(flags) || scap_dump_write(d, e, e->len) != e->len || scap_write_padding(d, sizeof(cpuid) + e->len) != SCAP_SUCCESS || scap_dump_write(d, &bt, sizeof(bt)) != sizeof(bt)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (7)"); return SCAP_FAILURE; } } // // Enable this to make sure that everything is saved to disk during the tests // #if 0 fflush(f); #endif return SCAP_SUCCESS; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // READ FUNCTIONS /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // Load the machine info block // static int32_t scap_read_machine_info(scap_t *handle, gzFile f, uint32_t block_length) { // // Read the section header block // if(gzread(f, &handle->m_machine_info, sizeof(handle->m_machine_info)) != sizeof(handle->m_machine_info)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading from file (1)"); return SCAP_FAILURE; } return SCAP_SUCCESS; } // // Parse a process list block // static int32_t scap_read_proclist(scap_t *handle, gzFile f, uint32_t block_length, uint32_t block_type) { size_t readsize; size_t subreadsize = 0; size_t totreadsize = 0; size_t padding_len; uint16_t stlen; uint32_t padding; int32_t uth_status = SCAP_SUCCESS; uint32_t toread; int fseekres; while(((int32_t)block_length - (int32_t)totreadsize) >= 4) { struct scap_threadinfo tinfo; tinfo.fdlist = NULL; tinfo.flags = 0; tinfo.vmsize_kb = 0; tinfo.vmrss_kb = 0; tinfo.vmswap_kb = 0; tinfo.pfmajor = 0; tinfo.pfminor = 0; tinfo.env_len = 0; tinfo.vtid = -1; tinfo.vpid = -1; tinfo.cgroups_len = 0; tinfo.filtered_out = 0; tinfo.root[0] = 0; tinfo.sid = -1; tinfo.vpgid = -1; tinfo.clone_ts = 0; tinfo.tty = 0; tinfo.exepath[0] = 0; tinfo.loginuid = -1; // // len // uint32_t sub_len = 0; switch(block_type) { case PL_BLOCK_TYPE_V1: case PL_BLOCK_TYPE_V1_INT: case PL_BLOCK_TYPE_V2: case PL_BLOCK_TYPE_V2_INT: case PL_BLOCK_TYPE_V3: case PL_BLOCK_TYPE_V3_INT: case PL_BLOCK_TYPE_V4: case PL_BLOCK_TYPE_V5: case PL_BLOCK_TYPE_V6: case PL_BLOCK_TYPE_V7: case PL_BLOCK_TYPE_V8: break; case PL_BLOCK_TYPE_V9: readsize = gzread(f, &(sub_len), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); subreadsize += readsize; break; default: snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted process block type (fd1)"); ASSERT(false); return SCAP_FAILURE; } // // tid // readsize = gzread(f, &(tinfo.tid), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); subreadsize += readsize; // // pid // readsize = gzread(f, &(tinfo.pid), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); subreadsize += readsize; // // ptid // readsize = gzread(f, &(tinfo.ptid), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); subreadsize += readsize; switch(block_type) { case PL_BLOCK_TYPE_V1: case PL_BLOCK_TYPE_V1_INT: case PL_BLOCK_TYPE_V2: case PL_BLOCK_TYPE_V2_INT: case PL_BLOCK_TYPE_V3: case PL_BLOCK_TYPE_V3_INT: case PL_BLOCK_TYPE_V4: case PL_BLOCK_TYPE_V5: break; case PL_BLOCK_TYPE_V6: case PL_BLOCK_TYPE_V7: case PL_BLOCK_TYPE_V8: case PL_BLOCK_TYPE_V9: readsize = gzread(f, &(tinfo.sid), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); subreadsize += readsize; break; default: snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted process block type (fd1)"); ASSERT(false); return SCAP_FAILURE; } // // vpgid // switch(block_type) { case PL_BLOCK_TYPE_V1: case PL_BLOCK_TYPE_V1_INT: case PL_BLOCK_TYPE_V2: case PL_BLOCK_TYPE_V2_INT: case PL_BLOCK_TYPE_V3: case PL_BLOCK_TYPE_V3_INT: case PL_BLOCK_TYPE_V4: case PL_BLOCK_TYPE_V5: case PL_BLOCK_TYPE_V6: case PL_BLOCK_TYPE_V7: break; case PL_BLOCK_TYPE_V8: case PL_BLOCK_TYPE_V9: readsize = gzread(f, &(tinfo.vpgid), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); subreadsize += readsize; break; default: snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted process block type (fd1)"); ASSERT(false); return SCAP_FAILURE; } // // comm // readsize = gzread(f, &(stlen), sizeof(uint16_t)); CHECK_READ_SIZE(readsize, sizeof(uint16_t)); if(stlen > SCAP_MAX_PATH_SIZE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid commlen %d", stlen); return SCAP_FAILURE; } subreadsize += readsize; readsize = gzread(f, tinfo.comm, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file tinfo.comm[stlen] = 0; subreadsize += readsize; // // exe // readsize = gzread(f, &(stlen), sizeof(uint16_t)); CHECK_READ_SIZE(readsize, sizeof(uint16_t)); if(stlen > SCAP_MAX_PATH_SIZE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid exelen %d", stlen); return SCAP_FAILURE; } subreadsize += readsize; readsize = gzread(f, tinfo.exe, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file tinfo.exe[stlen] = 0; subreadsize += readsize; switch(block_type) { case PL_BLOCK_TYPE_V1: case PL_BLOCK_TYPE_V1_INT: case PL_BLOCK_TYPE_V2: case PL_BLOCK_TYPE_V2_INT: case PL_BLOCK_TYPE_V3: case PL_BLOCK_TYPE_V3_INT: case PL_BLOCK_TYPE_V4: case PL_BLOCK_TYPE_V5: case PL_BLOCK_TYPE_V6: break; case PL_BLOCK_TYPE_V7: case PL_BLOCK_TYPE_V8: case PL_BLOCK_TYPE_V9: // // exepath // readsize = gzread(f, &(stlen), sizeof(uint16_t)); CHECK_READ_SIZE(readsize, sizeof(uint16_t)); if(stlen > SCAP_MAX_PATH_SIZE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid exepathlen %d", stlen); return SCAP_FAILURE; } subreadsize += readsize; readsize = gzread(f, tinfo.exepath, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file tinfo.exepath[stlen] = 0; subreadsize += readsize; break; default: snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted process block type (fd1)"); ASSERT(false); return SCAP_FAILURE; } // // args // readsize = gzread(f, &(stlen), sizeof(uint16_t)); CHECK_READ_SIZE(readsize, sizeof(uint16_t)); if(stlen > SCAP_MAX_ARGS_SIZE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid argslen %d", stlen); return SCAP_FAILURE; } subreadsize += readsize; readsize = gzread(f, tinfo.args, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file tinfo.args[stlen] = 0; tinfo.args_len = stlen; subreadsize += readsize; // // cwd // readsize = gzread(f, &(stlen), sizeof(uint16_t)); CHECK_READ_SIZE(readsize, sizeof(uint16_t)); if(stlen > SCAP_MAX_PATH_SIZE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid cwdlen %d", stlen); return SCAP_FAILURE; } subreadsize += readsize; readsize = gzread(f, tinfo.cwd, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file tinfo.cwd[stlen] = 0; subreadsize += readsize; // // fdlimit // readsize = gzread(f, &(tinfo.fdlimit), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); subreadsize += readsize; // // flags // readsize = gzread(f, &(tinfo.flags), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); subreadsize += readsize; // // uid // readsize = gzread(f, &(tinfo.uid), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); subreadsize += readsize; // // gid // readsize = gzread(f, &(tinfo.gid), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); subreadsize += readsize; switch(block_type) { case PL_BLOCK_TYPE_V1: case PL_BLOCK_TYPE_V1_INT: break; case PL_BLOCK_TYPE_V2: case PL_BLOCK_TYPE_V2_INT: case PL_BLOCK_TYPE_V3: case PL_BLOCK_TYPE_V3_INT: case PL_BLOCK_TYPE_V4: case PL_BLOCK_TYPE_V5: case PL_BLOCK_TYPE_V6: case PL_BLOCK_TYPE_V7: case PL_BLOCK_TYPE_V8: case PL_BLOCK_TYPE_V9: // // vmsize_kb // readsize = gzread(f, &(tinfo.vmsize_kb), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); subreadsize += readsize; // // vmrss_kb // readsize = gzread(f, &(tinfo.vmrss_kb), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); subreadsize += readsize; // // vmswap_kb // readsize = gzread(f, &(tinfo.vmswap_kb), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); subreadsize += readsize; // // pfmajor // readsize = gzread(f, &(tinfo.pfmajor), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); subreadsize += readsize; // // pfminor // readsize = gzread(f, &(tinfo.pfminor), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); subreadsize += readsize; if(block_type == PL_BLOCK_TYPE_V3 || block_type == PL_BLOCK_TYPE_V3_INT || block_type == PL_BLOCK_TYPE_V4 || block_type == PL_BLOCK_TYPE_V5 || block_type == PL_BLOCK_TYPE_V6 || block_type == PL_BLOCK_TYPE_V7 || block_type == PL_BLOCK_TYPE_V8 || block_type == PL_BLOCK_TYPE_V9) { // // env // readsize = gzread(f, &(stlen), sizeof(uint16_t)); CHECK_READ_SIZE(readsize, sizeof(uint16_t)); if(stlen > SCAP_MAX_ENV_SIZE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid envlen %d", stlen); return SCAP_FAILURE; } subreadsize += readsize; readsize = gzread(f, tinfo.env, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file tinfo.env[stlen] = 0; tinfo.env_len = stlen; subreadsize += readsize; } if(block_type == PL_BLOCK_TYPE_V4 || block_type == PL_BLOCK_TYPE_V5 || block_type == PL_BLOCK_TYPE_V6 || block_type == PL_BLOCK_TYPE_V7 || block_type == PL_BLOCK_TYPE_V8 || block_type == PL_BLOCK_TYPE_V9) { // // vtid // readsize = gzread(f, &(tinfo.vtid), sizeof(int64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); subreadsize += readsize; // // vpid // readsize = gzread(f, &(tinfo.vpid), sizeof(int64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); subreadsize += readsize; // // cgroups // readsize = gzread(f, &(stlen), sizeof(uint16_t)); CHECK_READ_SIZE(readsize, sizeof(uint16_t)); if(stlen > SCAP_MAX_CGROUPS_SIZE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid cgroupslen %d", stlen); return SCAP_FAILURE; } tinfo.cgroups_len = stlen; subreadsize += readsize; readsize = gzread(f, tinfo.cgroups, stlen); CHECK_READ_SIZE(readsize, stlen); subreadsize += readsize; if(block_type == PL_BLOCK_TYPE_V5 || block_type == PL_BLOCK_TYPE_V6 || block_type == PL_BLOCK_TYPE_V7 || block_type == PL_BLOCK_TYPE_V8 || block_type == PL_BLOCK_TYPE_V9) { readsize = gzread(f, &(stlen), sizeof(uint16_t)); CHECK_READ_SIZE(readsize, sizeof(uint16_t)); if(stlen > SCAP_MAX_PATH_SIZE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid rootlen %d", stlen); return SCAP_FAILURE; } subreadsize += readsize; readsize = gzread(f, tinfo.root, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file tinfo.root[stlen] = 0; subreadsize += readsize; } } break; default: snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted process block type (fd1)"); ASSERT(false); return SCAP_FAILURE; } // If new parameters are added, sub_len can be used to // see if they are available in the current capture. // For example, for a 32bit parameter: // // if(sub_len && (subreadsize + sizeof(uint32_t)) <= sub_len) // { // ... // } // // loginuid // if(sub_len && (subreadsize + sizeof(int32_t)) <= sub_len) { readsize = gzread(f, &(tinfo.loginuid), sizeof(int32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); subreadsize += readsize; } // // All parsed. Add the entry to the table, or fire the notification callback // if(handle->m_proc_callback == NULL) { // // All parsed. Allocate the new entry and copy the temp one into into it. // struct scap_threadinfo *ntinfo = (scap_threadinfo *)malloc(sizeof(scap_threadinfo)); if(ntinfo == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "process table allocation error (fd1)"); return SCAP_FAILURE; } // Structure copy *ntinfo = tinfo; HASH_ADD_INT64(handle->m_proclist, tid, ntinfo); if(uth_status != SCAP_SUCCESS) { free(ntinfo); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "process table allocation error (fd2)"); return SCAP_FAILURE; } } else { handle->m_proc_callback(handle->m_proc_callback_context, handle, tinfo.tid, &tinfo, NULL); } if(sub_len && subreadsize != sub_len) { if(subreadsize > sub_len) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Had read %lu bytes, but proclist entry have length %u.", subreadsize, sub_len); return SCAP_FAILURE; } toread = sub_len - subreadsize; fseekres = (int)gzseek(f, (long)toread, SEEK_CUR); if(fseekres == -1) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Can't skip %u bytes.", (unsigned int)toread); return SCAP_FAILURE; } subreadsize = sub_len; } totreadsize += subreadsize; subreadsize = 0; } // // Read the padding bytes so we properly align to the end of the data // if(totreadsize > block_length) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_read_proclist read more %lu than a block %u", totreadsize, block_length); ASSERT(false); return SCAP_FAILURE; } padding_len = block_length - totreadsize; readsize = (size_t)gzread(f, &padding, (unsigned int)padding_len); CHECK_READ_SIZE(readsize, padding_len); return SCAP_SUCCESS; } // // Parse an interface list block // static int32_t scap_read_iflist(scap_t *handle, gzFile f, uint32_t block_length, uint32_t block_type) { int32_t res = SCAP_SUCCESS; size_t readsize; size_t totreadsize; char *readbuf = NULL; char *pif; uint16_t iftype; uint16_t ifnamlen; uint32_t toread; uint32_t entrysize; uint32_t ifcnt4 = 0; uint32_t ifcnt6 = 0; // // If the list of interfaces was already allocated for this handle (for example because this is // not the first interface list block), free it // if(handle->m_addrlist != NULL) { scap_free_iflist(handle->m_addrlist); handle->m_addrlist = NULL; } // // Bring the block to memory // We assume that this block is always small enough that we can read it in a single shot // readbuf = (char *)malloc(block_length); if(!readbuf) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "memory allocation error in scap_read_iflist"); return SCAP_FAILURE; } readsize = gzread(f, readbuf, block_length); CHECK_READ_SIZE_WITH_FREE(readbuf, readsize, block_length); // // First pass, count the number of addresses // pif = readbuf; totreadsize = 0; while(true) { toread = (int32_t)block_length - (int32_t)totreadsize; if(toread < 4) { break; } if(block_type != IL_BLOCK_TYPE_V2) { iftype = *(uint16_t *)pif; ifnamlen = *(uint16_t *)(pif + 2); if(iftype == SCAP_II_IPV4) { entrysize = sizeof(scap_ifinfo_ipv4) + ifnamlen - SCAP_MAX_PATH_SIZE; } else if(iftype == SCAP_II_IPV6) { entrysize = sizeof(scap_ifinfo_ipv6) + ifnamlen - SCAP_MAX_PATH_SIZE; } else if(iftype == SCAP_II_IPV4_NOLINKSPEED) { entrysize = sizeof(scap_ifinfo_ipv4_nolinkspeed) + ifnamlen - SCAP_MAX_PATH_SIZE; } else if(iftype == SCAP_II_IPV6_NOLINKSPEED) { entrysize = sizeof(scap_ifinfo_ipv6_nolinkspeed) + ifnamlen - SCAP_MAX_PATH_SIZE; } else { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "trace file has corrupted interface list(1)"); ASSERT(false); res = SCAP_FAILURE; goto scap_read_iflist_error; } } else { entrysize = *(uint32_t *)pif + sizeof(uint32_t); iftype = *(uint16_t *)(pif + 4); ifnamlen = *(uint16_t *)(pif + 4 + 2); } if(toread < entrysize) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "trace file has corrupted interface list(2) toread=%u, entrysize=%u", toread, entrysize); res = SCAP_FAILURE; goto scap_read_iflist_error; } pif += entrysize; totreadsize += entrysize; if(iftype == SCAP_II_IPV4 || iftype == SCAP_II_IPV4_NOLINKSPEED) { ifcnt4++; } else if(iftype == SCAP_II_IPV6 || iftype == SCAP_II_IPV6_NOLINKSPEED) { ifcnt6++; } else { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "unknown interface type %d", (int)iftype); res = SCAP_FAILURE; goto scap_read_iflist_error; } } // // Allocate the handle and the arrays // handle->m_addrlist = (scap_addrlist *)malloc(sizeof(scap_addrlist)); if(!handle->m_addrlist) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_read_iflist allocation failed(1)"); res = SCAP_FAILURE; goto scap_read_iflist_error; } handle->m_addrlist->n_v4_addrs = 0; handle->m_addrlist->n_v6_addrs = 0; handle->m_addrlist->v4list = NULL; handle->m_addrlist->v6list = NULL; handle->m_addrlist->totlen = block_length - (ifcnt4 + ifcnt6) * sizeof(uint32_t); if(ifcnt4 != 0) { handle->m_addrlist->v4list = (scap_ifinfo_ipv4 *)malloc(ifcnt4 * sizeof(scap_ifinfo_ipv4)); if(!handle->m_addrlist->v4list) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_read_iflist allocation failed(2)"); res = SCAP_FAILURE; goto scap_read_iflist_error; } } else { handle->m_addrlist->v4list = NULL; } if(ifcnt6 != 0) { handle->m_addrlist->v6list = (scap_ifinfo_ipv6 *)malloc(ifcnt6 * sizeof(scap_ifinfo_ipv6)); if(!handle->m_addrlist->v6list) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "getifaddrs allocation failed(3)"); res = SCAP_FAILURE; goto scap_read_iflist_error; } } else { handle->m_addrlist->v6list = NULL; } handle->m_addrlist->n_v4_addrs = ifcnt4; handle->m_addrlist->n_v6_addrs = ifcnt6; // // Second pass: populate the arrays // ifcnt4 = 0; ifcnt6 = 0; pif = readbuf; totreadsize = 0; while(true) { toread = (int32_t)block_length - (int32_t)totreadsize; entrysize = 0; if(toread < 4) { break; } if(block_type == IL_BLOCK_TYPE_V2) { entrysize = *(uint32_t *)pif; totreadsize += sizeof(uint32_t); pif += sizeof(uint32_t); } iftype = *(uint16_t *)pif; ifnamlen = *(uint16_t *)(pif + 2); if(ifnamlen >= SCAP_MAX_PATH_SIZE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "trace file has corrupted interface list(0)"); res = SCAP_FAILURE; goto scap_read_iflist_error; } // If new parameters are added, entrysize can be used to // see if they are available in the current capture. // For example, for a 32bit parameter: // // if(entrysize && (ifsize + sizeof(uint32_t)) <= entrysize) // { // ifsize += sizeof(uint32_t); // ... // } uint32_t ifsize; if(iftype == SCAP_II_IPV4) { ifsize = sizeof(uint16_t) + // type sizeof(uint16_t) + // ifnamelen sizeof(uint32_t) + // addr sizeof(uint32_t) + // netmask sizeof(uint32_t) + // bcast sizeof(uint64_t) + // linkspeed ifnamlen; if(toread < ifsize) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "trace file has corrupted interface list(3)"); res = SCAP_FAILURE; goto scap_read_iflist_error; } // Copy the entry memcpy(handle->m_addrlist->v4list + ifcnt4, pif, ifsize - ifnamlen); memcpy(handle->m_addrlist->v4list[ifcnt4].ifname, pif + ifsize - ifnamlen, ifnamlen); // Make sure the name string is NULL-terminated *((char *)(handle->m_addrlist->v4list + ifcnt4) + ifsize) = 0; ifcnt4++; } else if(iftype == SCAP_II_IPV4_NOLINKSPEED) { scap_ifinfo_ipv4_nolinkspeed* src; scap_ifinfo_ipv4* dst; ifsize = sizeof(scap_ifinfo_ipv4_nolinkspeed) + ifnamlen - SCAP_MAX_PATH_SIZE; if(toread < ifsize) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "trace file has corrupted interface list(4)"); res = SCAP_FAILURE; goto scap_read_iflist_error; } // Copy the entry src = (scap_ifinfo_ipv4_nolinkspeed*)pif; dst = handle->m_addrlist->v4list + ifcnt4; dst->type = src->type; dst->ifnamelen = src->ifnamelen; dst->addr = src->addr; dst->netmask = src->netmask; dst->bcast = src->bcast; dst->linkspeed = 0; memcpy(dst->ifname, src->ifname, MIN(dst->ifnamelen, SCAP_MAX_PATH_SIZE - 1)); // Make sure the name string is NULL-terminated *((char *)(dst->ifname + MIN(dst->ifnamelen, SCAP_MAX_PATH_SIZE - 1))) = 0; ifcnt4++; } else if(iftype == SCAP_II_IPV6) { ifsize = sizeof(uint16_t) + // type sizeof(uint16_t) + // ifnamelen SCAP_IPV6_ADDR_LEN + // addr SCAP_IPV6_ADDR_LEN + // netmask SCAP_IPV6_ADDR_LEN + // bcast sizeof(uint64_t) + // linkspeed ifnamlen; if(toread < ifsize) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "trace file has corrupted interface list(5)"); res = SCAP_FAILURE; goto scap_read_iflist_error; } // Copy the entry memcpy(handle->m_addrlist->v6list + ifcnt6, pif, ifsize - ifnamlen); memcpy(handle->m_addrlist->v6list[ifcnt6].ifname, pif + ifsize - ifnamlen, ifnamlen); // Make sure the name string is NULL-terminated *((char *)(handle->m_addrlist->v6list + ifcnt6) + ifsize) = 0; ifcnt6++; } else if(iftype == SCAP_II_IPV6_NOLINKSPEED) { scap_ifinfo_ipv6_nolinkspeed* src; scap_ifinfo_ipv6* dst; ifsize = sizeof(scap_ifinfo_ipv6_nolinkspeed) + ifnamlen - SCAP_MAX_PATH_SIZE; if(toread < ifsize) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "trace file has corrupted interface list(6)"); res = SCAP_FAILURE; goto scap_read_iflist_error; } // Copy the entry src = (scap_ifinfo_ipv6_nolinkspeed*)pif; dst = handle->m_addrlist->v6list + ifcnt6; dst->type = src->type; dst->ifnamelen = src->ifnamelen; memcpy(dst->addr, src->addr, SCAP_IPV6_ADDR_LEN); memcpy(dst->netmask, src->netmask, SCAP_IPV6_ADDR_LEN); memcpy(dst->bcast, src->bcast, SCAP_IPV6_ADDR_LEN); dst->linkspeed = 0; memcpy(dst->ifname, src->ifname, MIN(dst->ifnamelen, SCAP_MAX_PATH_SIZE - 1)); // Make sure the name string is NULL-terminated *((char *)(dst->ifname + MIN(dst->ifnamelen, SCAP_MAX_PATH_SIZE - 1))) = 0; ifcnt6++; } else { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "unknown interface type %d", (int)iftype); res = SCAP_FAILURE; goto scap_read_iflist_error; } entrysize = entrysize ? entrysize : ifsize; pif += entrysize; totreadsize += entrysize; } // // Release the read storage // free(readbuf); return res; scap_read_iflist_error: scap_free_iflist(handle->m_addrlist); handle->m_addrlist = NULL; if(readbuf) { free(readbuf); } return res; } // // Parse a user list block // static int32_t scap_read_userlist(scap_t *handle, gzFile f, uint32_t block_length, uint32_t block_type) { size_t readsize; size_t totreadsize = 0; size_t subreadsize = 0; size_t padding_len; uint32_t padding; uint8_t type; uint16_t stlen; uint32_t toread; int fseekres; // // If the list of users was already allocated for this handle (for example because this is // not the first interface list block), free it // if(handle->m_userlist != NULL) { scap_free_userlist(handle->m_userlist); handle->m_userlist = NULL; } // // Allocate and initialize the handle info // handle->m_userlist = (scap_userlist*)malloc(sizeof(scap_userlist)); if(handle->m_userlist == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "userlist allocation failed(2)"); return SCAP_FAILURE; } handle->m_userlist->nusers = 0; handle->m_userlist->ngroups = 0; handle->m_userlist->totsavelen = 0; handle->m_userlist->users = NULL; handle->m_userlist->groups = NULL; // // Import the blocks // while(((int32_t)block_length - (int32_t)totreadsize) >= 4) { uint32_t sub_len = 0; if(block_type == UL_BLOCK_TYPE_V2) { // // len // readsize = gzread(f, &(sub_len), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); subreadsize += readsize; } // // type // readsize = gzread(f, &(type), sizeof(type)); CHECK_READ_SIZE(readsize, sizeof(type)); subreadsize += readsize; if(type == USERBLOCK_TYPE_USER) { scap_userinfo* puser; handle->m_userlist->nusers++; handle->m_userlist->users = (scap_userinfo*)realloc(handle->m_userlist->users, handle->m_userlist->nusers * sizeof(scap_userinfo)); if(handle->m_userlist->users == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "memory allocation error in scap_read_userlist(1)"); return SCAP_FAILURE; } puser = &handle->m_userlist->users[handle->m_userlist->nusers -1]; // // uid // readsize = gzread(f, &(puser->uid), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); subreadsize += readsize; // // gid // readsize = gzread(f, &(puser->gid), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); subreadsize += readsize; // // name // readsize = gzread(f, &(stlen), sizeof(uint16_t)); CHECK_READ_SIZE(readsize, sizeof(uint16_t)); if(stlen >= MAX_CREDENTIALS_STR_LEN) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid user name len %d", stlen); return SCAP_FAILURE; } subreadsize += readsize; readsize = gzread(f, puser->name, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file puser->name[stlen] = 0; subreadsize += readsize; // // homedir // readsize = gzread(f, &(stlen), sizeof(uint16_t)); CHECK_READ_SIZE(readsize, sizeof(uint16_t)); if(stlen >= MAX_CREDENTIALS_STR_LEN) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid user homedir len %d", stlen); return SCAP_FAILURE; } subreadsize += readsize; readsize = gzread(f, puser->homedir, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file puser->homedir[stlen] = 0; subreadsize += readsize; // // shell // readsize = gzread(f, &(stlen), sizeof(uint16_t)); CHECK_READ_SIZE(readsize, sizeof(uint16_t)); if(stlen >= MAX_CREDENTIALS_STR_LEN) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid user shell len %d", stlen); return SCAP_FAILURE; } subreadsize += readsize; readsize = gzread(f, puser->shell, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file puser->shell[stlen] = 0; subreadsize += readsize; // If new parameters are added, sub_len can be used to // see if they are available in the current capture. // For example, for a 32bit parameter: // // if(sub_len && (subreadsize + sizeof(uint32_t)) <= sub_len) // { // ... // } } else { scap_groupinfo* pgroup; handle->m_userlist->ngroups++; handle->m_userlist->groups = (scap_groupinfo*)realloc(handle->m_userlist->groups, handle->m_userlist->ngroups * sizeof(scap_groupinfo)); if(handle->m_userlist->groups == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "memory allocation error in scap_read_userlist(2)"); return SCAP_FAILURE; } pgroup = &handle->m_userlist->groups[handle->m_userlist->ngroups -1]; // // gid // readsize = gzread(f, &(pgroup->gid), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); subreadsize += readsize; // // name // readsize = gzread(f, &(stlen), sizeof(uint16_t)); CHECK_READ_SIZE(readsize, sizeof(uint16_t)); if(stlen >= MAX_CREDENTIALS_STR_LEN) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid group name len %d", stlen); return SCAP_FAILURE; } subreadsize += readsize; readsize = gzread(f, pgroup->name, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file pgroup->name[stlen] = 0; subreadsize += readsize; // If new parameters are added, sub_len can be used to // see if they are available in the current capture. // For example, for a 32bit parameter: // // if(sub_len && (subreadsize + sizeof(uint32_t)) <= sub_len) // { // ... // } } if(sub_len && subreadsize != sub_len) { if(subreadsize > sub_len) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Had read %lu bytes, but userlist entry have length %u.", subreadsize, sub_len); return SCAP_FAILURE; } toread = sub_len - subreadsize; fseekres = (int)gzseek(f, (long)toread, SEEK_CUR); if(fseekres == -1) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Can't skip %u bytes.", (unsigned int)toread); return SCAP_FAILURE; } subreadsize = sub_len; } totreadsize += subreadsize; subreadsize = 0; } // // Read the padding bytes so we properly align to the end of the data // if(totreadsize > block_length) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_read_userlist read more %lu than a block %u", totreadsize, block_length); return SCAP_FAILURE; } padding_len = block_length - totreadsize; readsize = gzread(f, &padding, (unsigned int)padding_len); CHECK_READ_SIZE(readsize, padding_len); return SCAP_SUCCESS; } // // Parse a process list block // static int32_t scap_read_fdlist(scap_t *handle, gzFile f, uint32_t block_length, uint32_t block_type) { size_t readsize; size_t totreadsize = 0; size_t padding_len; struct scap_threadinfo *tinfo; scap_fdinfo fdi; scap_fdinfo *nfdi; // uint16_t stlen; uint64_t tid; int32_t uth_status = SCAP_SUCCESS; uint32_t padding; // // Read the tid // readsize = gzread(f, &tid, sizeof(tid)); CHECK_READ_SIZE(readsize, sizeof(tid)); totreadsize += readsize; if(handle->m_proc_callback == NULL) { // // Identify the process descriptor // HASH_FIND_INT64(handle->m_proclist, &tid, tinfo); if(tinfo == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted trace file. FD block references TID %"PRIu64", which doesn't exist.", tid); return SCAP_FAILURE; } } else { tinfo = NULL; } while(((int32_t)block_length - (int32_t)totreadsize) >= 4) { if(scap_fd_read_from_disk(handle, &fdi, &readsize, block_type, f) != SCAP_SUCCESS) { return SCAP_FAILURE; } totreadsize += readsize; // // Add the entry to the table, or fire the notification callback // if(handle->m_proc_callback == NULL) { // // Parsed successfully. Allocate the new entry and copy the temp one into into it. // nfdi = (scap_fdinfo *)malloc(sizeof(scap_fdinfo)); if(nfdi == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "process table allocation error (fd1)"); return SCAP_FAILURE; } // Structure copy *nfdi = fdi; ASSERT(tinfo != NULL); HASH_ADD_INT64(tinfo->fdlist, fd, nfdi); if(uth_status != SCAP_SUCCESS) { free(nfdi); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "process table allocation error (fd2)"); return SCAP_FAILURE; } } else { ASSERT(tinfo == NULL); handle->m_proc_callback(handle->m_proc_callback_context, handle, tid, NULL, &fdi); } } // // Read the padding bytes so we properly align to the end of the data // if(totreadsize > block_length) { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_read_fdlist read more %lu than a block %u", totreadsize, block_length); return SCAP_FAILURE; } padding_len = block_length - totreadsize; readsize = gzread(f, &padding, (unsigned int)padding_len); CHECK_READ_SIZE(readsize, padding_len); return SCAP_SUCCESS; } // // Parse the headers of a trace file and load the tables // int32_t scap_read_init(scap_t *handle, gzFile f) { block_header bh; section_header_block sh; uint32_t bt; size_t readsize; size_t toread; int fseekres; int8_t found_mi = 0; int8_t found_pl = 0; int8_t found_fdl = 0; int8_t found_il = 0; int8_t found_ul = 0; int8_t found_ev = 0; // // Read the section header block // if(gzread(f, &bh, sizeof(bh)) != sizeof(bh) || gzread(f, &sh, sizeof(sh)) != sizeof(sh) || gzread(f, &bt, sizeof(bt)) != sizeof(bt)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading from file (1)"); return SCAP_FAILURE; } if(bh.block_type != SHB_BLOCK_TYPE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid block type"); return SCAP_FAILURE; } if(sh.byte_order_magic != 0x1a2b3c4d) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "invalid magic number"); return SCAP_FAILURE; } if(sh.major_version > CURRENT_MAJOR_VERSION) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "cannot correctly parse the capture. Upgrade your version of sysdig."); return SCAP_VERSION_MISMATCH; } // // Read the metadata blocks (processes, FDs, etc.) // while(true) { readsize = gzread(f, &bh, sizeof(bh)); // // If we don't find the event block header, // it means there is no event in the file. // if (readsize == 0 && !found_ev && found_mi && found_pl && found_il && found_fdl && found_ul) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "no events in file"); return SCAP_FAILURE; } CHECK_READ_SIZE(readsize, sizeof(bh)); switch(bh.block_type) { case MI_BLOCK_TYPE: case MI_BLOCK_TYPE_INT: found_mi = 1; if(scap_read_machine_info(handle, f, bh.block_total_length - sizeof(block_header) - 4) != SCAP_SUCCESS) { return SCAP_FAILURE; } break; case PL_BLOCK_TYPE_V1: case PL_BLOCK_TYPE_V2: case PL_BLOCK_TYPE_V3: case PL_BLOCK_TYPE_V4: case PL_BLOCK_TYPE_V5: case PL_BLOCK_TYPE_V6: case PL_BLOCK_TYPE_V7: case PL_BLOCK_TYPE_V8: case PL_BLOCK_TYPE_V9: case PL_BLOCK_TYPE_V1_INT: case PL_BLOCK_TYPE_V2_INT: case PL_BLOCK_TYPE_V3_INT: found_pl = 1; if(scap_read_proclist(handle, f, bh.block_total_length - sizeof(block_header) - 4, bh.block_type) != SCAP_SUCCESS) { return SCAP_FAILURE; } break; case FDL_BLOCK_TYPE: case FDL_BLOCK_TYPE_INT: case FDL_BLOCK_TYPE_V2: found_fdl = 1; if(scap_read_fdlist(handle, f, bh.block_total_length - sizeof(block_header) - 4, bh.block_type) != SCAP_SUCCESS) { return SCAP_FAILURE; } break; case EV_BLOCK_TYPE: case EV_BLOCK_TYPE_INT: case EV_BLOCK_TYPE_V2: case EVF_BLOCK_TYPE: case EVF_BLOCK_TYPE_V2: found_ev = 1; // // We're done with the metadata headers. Rewind the file position so we are aligned to start reading the events. // fseekres = gzseek(f, (long)0 - sizeof(bh), SEEK_CUR); if(fseekres != -1) { break; } else { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error seeking in file"); return SCAP_FAILURE; } case IL_BLOCK_TYPE: case IL_BLOCK_TYPE_INT: case IL_BLOCK_TYPE_V2: found_il = 1; if(scap_read_iflist(handle, f, bh.block_total_length - sizeof(block_header) - 4, bh.block_type) != SCAP_SUCCESS) { return SCAP_FAILURE; } break; case UL_BLOCK_TYPE: case UL_BLOCK_TYPE_INT: case UL_BLOCK_TYPE_V2: found_ul = 1; if(scap_read_userlist(handle, f, bh.block_total_length - sizeof(block_header) - 4, bh.block_type) != SCAP_SUCCESS) { return SCAP_FAILURE; } break; default: // // Unknwon block type. Skip the block. // toread = bh.block_total_length - sizeof(block_header) - 4; fseekres = (int)gzseek(f, (long)toread, SEEK_CUR); if(fseekres == -1) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Can't skip block of type %x and size %u.", (int)bh.block_type, (unsigned int)toread); return SCAP_FAILURE; } break; } if(found_ev) { break; } // // Read and validate the trailer // readsize = gzread(f, &bt, sizeof(bt)); CHECK_READ_SIZE(readsize, sizeof(bt)); if(bt != bh.block_total_length) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "wrong block total length, header=%u, trailer=%u", bh.block_total_length, bt); return SCAP_FAILURE; } } if(!found_mi) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Can't find machine info block."); return SCAP_FAILURE; } if(!found_ul) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Can't find user list block."); return SCAP_FAILURE; } if(!found_il) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Can't find interface list block."); return SCAP_FAILURE; } return SCAP_SUCCESS; } // // Read an event from disk // int32_t scap_next_offline(scap_t *handle, OUT scap_evt **pevent, OUT uint16_t *pcpuid) { block_header bh; size_t readsize; uint32_t readlen; size_t hdr_len; gzFile f = handle->m_file; ASSERT(f != NULL); // // We may have to repeat the whole process // if the capture contains new syscalls // while(true) { // // Read the block header // readsize = gzread(f, &bh, sizeof(bh)); if(readsize != sizeof(bh)) { int err_no = 0; const char* err_str = gzerror(f, &err_no); if(err_no) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error reading file: %s, ernum=%d", err_str, err_no); return SCAP_FAILURE; } if(readsize == 0) { // // We read exactly 0 bytes. This indicates a correct end of file. // return SCAP_EOF; } else { CHECK_READ_SIZE(readsize, sizeof(bh)); } } if(bh.block_type != EV_BLOCK_TYPE && bh.block_type != EV_BLOCK_TYPE_V2 && bh.block_type != EV_BLOCK_TYPE_INT && bh.block_type != EVF_BLOCK_TYPE && bh.block_type != EVF_BLOCK_TYPE_V2) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "unexpected block type %u", (uint32_t)bh.block_type); handle->m_unexpected_block_readsize = readsize; return SCAP_UNEXPECTED_BLOCK; } hdr_len = sizeof(struct ppm_evt_hdr); if(bh.block_type != EV_BLOCK_TYPE_V2 && bh.block_type != EVF_BLOCK_TYPE_V2) { hdr_len -= 4; } if(bh.block_total_length < sizeof(bh) + hdr_len + 4) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "block length too short %u", (uint32_t)bh.block_total_length); return SCAP_FAILURE; } // // Read the event // readlen = bh.block_total_length - sizeof(bh); if (readlen > FILE_READ_BUF_SIZE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "event block length %u greater than read buffer size %u", readlen, FILE_READ_BUF_SIZE); return SCAP_FAILURE; } readsize = gzread(f, handle->m_file_evt_buf, readlen); CHECK_READ_SIZE(readsize, readlen); // // EVF_BLOCK_TYPE has 32 bits of flags // *pcpuid = *(uint16_t *)handle->m_file_evt_buf; if(bh.block_type == EVF_BLOCK_TYPE || bh.block_type == EVF_BLOCK_TYPE_V2) { handle->m_last_evt_dump_flags = *(uint32_t*)(handle->m_file_evt_buf + sizeof(uint16_t)); *pevent = (struct ppm_evt_hdr *)(handle->m_file_evt_buf + sizeof(uint16_t) + sizeof(uint32_t)); } else { handle->m_last_evt_dump_flags = 0; *pevent = (struct ppm_evt_hdr *)(handle->m_file_evt_buf + sizeof(uint16_t)); } if((*pevent)->type >= PPM_EVENT_MAX) { // // We're reading a capture that contains new syscalls. // We can't do anything else that skips them. // continue; } if(bh.block_type != EV_BLOCK_TYPE_V2 && bh.block_type != EVF_BLOCK_TYPE_V2) { // // We're reading a old capture which events don't have nparams in the header. // Convert it to the current version. // if((readlen + sizeof(uint32_t)) > FILE_READ_BUF_SIZE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "cannot convert v1 event block to v2 (%lu greater than read buffer size %u)", readlen + sizeof(uint32_t), FILE_READ_BUF_SIZE); return SCAP_FAILURE; } memmove((char *)*pevent + sizeof(struct ppm_evt_hdr), (char *)*pevent + sizeof(struct ppm_evt_hdr) - sizeof(uint32_t), readlen - ((char *)*pevent - handle->m_file_evt_buf) - (sizeof(struct ppm_evt_hdr) - sizeof(uint32_t))); (*pevent)->len += sizeof(uint32_t); // In old captures, the length of PPME_NOTIFICATION_E and PPME_INFRASTRUCTURE_EVENT_E // is not correct. Adjust it, otherwise the following code will never find a match if((*pevent)->type == PPME_NOTIFICATION_E || (*pevent)->type == PPME_INFRASTRUCTURE_EVENT_E) { (*pevent)->len -= 3; } // // The number of parameters needs to be calculated based on the block len. // Use the current number of parameters as starting point and decrease it // until size matches. // char *end = (char *)*pevent + (*pevent)->len; uint16_t *lens = (uint16_t *)((char *)*pevent + sizeof(struct ppm_evt_hdr)); uint32_t nparams; bool done = false; for(nparams = g_event_info[(*pevent)->type].nparams; (int)nparams >= 0; nparams--) { char *valptr = (char *)lens + nparams * sizeof(uint16_t); if(valptr > end) { continue; } uint32_t i; for(i = 0; i < nparams; i++) { valptr += lens[i]; } if(valptr < end) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "cannot convert v1 event block to v2 (corrupted trace file - can't calculate nparams)."); return SCAP_FAILURE; } ASSERT(valptr >= end); if(valptr == end) { done = true; break; } } if(!done) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "cannot convert v1 event block to v2 (corrupted trace file - can't calculate nparams) (2)."); return SCAP_FAILURE; } (*pevent)->nparams = nparams; } break; } return SCAP_SUCCESS; } uint64_t scap_ftell(scap_t *handle) { gzFile f = handle->m_file; ASSERT(f != NULL); return gztell(f); } void scap_fseek(scap_t *handle, uint64_t off) { gzFile f = handle->m_file; ASSERT(f != NULL); gzseek(f, off, SEEK_SET); } sysdig-0.26.4/userspace/libscap/scap_savefile.h000066400000000000000000000135021352731327100214760ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // Force struct alignment #if defined _MSC_VER #pragma pack(push) #pragma pack(1) #elif defined __sun #pragma pack(1) #else #pragma pack(push, 1) #endif /////////////////////////////////////////////////////////////////////////////// // GENERIC BLOCK /////////////////////////////////////////////////////////////////////////////// typedef struct _block_header { uint32_t block_type; uint32_t block_total_length; // Block length, including this header and the trailing 32bits block length. }block_header; /////////////////////////////////////////////////////////////////////////////// // SECTION HEADER BLOCK /////////////////////////////////////////////////////////////////////////////// // Block type of the section header block #define SHB_BLOCK_TYPE 0x0A0D0D0A /*\r\n\n\r*/ // Magic of the section header block // Used to recognize if a section is in host byte order or not. #define SHB_MAGIC 0x1A2B3C4D // Major version of the file format supported by this library. // Must be increased only when if the new version of the software // is not able anymore to read older captures #define CURRENT_MAJOR_VERSION 1 // Minor version of the file format supported by this library. // We used to bump it every time the event table was updated, but // after adding {retro,forward} captures compatibility support // this is not required anymore. #define CURRENT_MINOR_VERSION 2 typedef struct _section_header_block { uint32_t byte_order_magic; uint16_t major_version; uint16_t minor_version; uint64_t section_length; }section_header_block; // NB: // Starting from scap version 1.2, block versions will no longer be changed. // New block fields must be appended and, instead of using the version, the // lengths of the sub blocks will be used to differentiate between versions. // For more infomation, look at the comments inside the various scap_read_* // functions. /////////////////////////////////////////////////////////////////////////////// // MACHINE INFO BLOCK /////////////////////////////////////////////////////////////////////////////// #define MI_BLOCK_TYPE 0x201 #define MI_BLOCK_TYPE_INT 0x8002ABCD // This is the unofficial number used before the // library release. We'll keep him for a while for // backward compatibility /////////////////////////////////////////////////////////////////////////////// // PROCESS LIST BLOCK /////////////////////////////////////////////////////////////////////////////// #define PL_BLOCK_TYPE_V1 0x202 #define PL_BLOCK_TYPE_V1_INT 0x8000ABCD // This is the unofficial number used before the // library release. We'll keep him for a while for // backward compatibility #define PL_BLOCK_TYPE_V2 0x207 #define PL_BLOCK_TYPE_V2_INT 0x8013ABCD // This is the unofficial number used before the // library release. We'll keep him for a while for // backward compatibility #define PL_BLOCK_TYPE_V3 0x209 #define PL_BLOCK_TYPE_V3_INT 0x8014ABCD // This is the unofficial number used before the // library release. We'll keep him for a while for // backward compatibility #define PL_BLOCK_TYPE_V4 0x210 #define PL_BLOCK_TYPE_V5 0x211 #define PL_BLOCK_TYPE_V6 0x212 #define PL_BLOCK_TYPE_V7 0x213 #define PL_BLOCK_TYPE_V8 0x214 #define PL_BLOCK_TYPE_V9 0x215 /////////////////////////////////////////////////////////////////////////////// // FD LIST BLOCK /////////////////////////////////////////////////////////////////////////////// #define FDL_BLOCK_TYPE 0x203 #define FDL_BLOCK_TYPE_INT 0x8001ABCD // This is the unofficial number used before the // library release. We'll keep him for a while for // backward compatibility #define FDL_BLOCK_TYPE_V2 0x218 /////////////////////////////////////////////////////////////////////////////// // EVENT BLOCK /////////////////////////////////////////////////////////////////////////////// #define EV_BLOCK_TYPE 0x204 #define EV_BLOCK_TYPE_INT 0x8010ABCD // This is the unofficial number used before the // library release. We'll keep him for a while for // backward compatibility #define EV_BLOCK_TYPE_V2 0x216 /////////////////////////////////////////////////////////////////////////////// // INTERFACE LIST BLOCK /////////////////////////////////////////////////////////////////////////////// #define IL_BLOCK_TYPE 0x205 #define IL_BLOCK_TYPE_INT 0x8011ABCD // This is the unofficial number used before the // library release. We'll keep him for a while for // backward compatibility #define IL_BLOCK_TYPE_V2 0x219 /////////////////////////////////////////////////////////////////////////////// // USER LIST BLOCK /////////////////////////////////////////////////////////////////////////////// #define UL_BLOCK_TYPE 0x206 #define UL_BLOCK_TYPE_INT 0x8012ABCD // This is the unofficial number used before the // library release. We'll keep him for a while for // backward compatibility #define UL_BLOCK_TYPE_V2 0x220 /////////////////////////////////////////////////////////////////////////////// // EVENT BLOCK WITH FLAGS /////////////////////////////////////////////////////////////////////////////// #define EVF_BLOCK_TYPE 0x208 #define EVF_BLOCK_TYPE_V2 0x217 #if defined __sun #pragma pack() #else #pragma pack(pop) #endif sysdig-0.26.4/userspace/libscap/scap_userlist.c000066400000000000000000000103651352731327100215510ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "scap.h" #include "scap-int.h" #if defined(HAS_CAPTURE) #include #include #include // // Allocate and return the list of users on this system // int32_t scap_create_userlist(scap_t* handle) { uint32_t usercnt; uint32_t grpcnt; struct passwd *p; struct group *g; // // If the list of users was already allocated for this handle (for example because this is // not the first user list block), free it // if(handle->m_userlist != NULL) { scap_free_userlist(handle->m_userlist); handle->m_userlist = NULL; } // // First pass: count the number of users and the number of groups // setpwent(); p = getpwent(); for(usercnt = 0; p; p = getpwent(), usercnt++); endpwent(); setgrent(); g = getgrent(); for(grpcnt = 0; g; g = getgrent(), grpcnt++); endgrent(); // // Memory allocations // handle->m_userlist = (scap_userlist*)malloc(sizeof(scap_userlist)); if(handle->m_userlist == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "userlist allocation failed(1)"); return SCAP_FAILURE; } handle->m_userlist->nusers = usercnt; handle->m_userlist->ngroups = grpcnt; handle->m_userlist->totsavelen = 0; handle->m_userlist->users = (scap_userinfo*)malloc(usercnt * sizeof(scap_userinfo)); if(handle->m_userlist->users == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "userlist allocation failed(2)"); free(handle->m_userlist); return SCAP_FAILURE; } handle->m_userlist->groups = (scap_groupinfo*)malloc(grpcnt * sizeof(scap_groupinfo)); if(handle->m_userlist->groups == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "grouplist allocation failed(2)"); free(handle->m_userlist->users); free(handle->m_userlist); return SCAP_FAILURE; } // // Second pass: copy the data // //users setpwent(); p = getpwent(); for(usercnt = 0; p; p = getpwent(), usercnt++) { handle->m_userlist->users[usercnt].uid = p->pw_uid; handle->m_userlist->users[usercnt].gid = p->pw_gid; if(p->pw_name) { strncpy(handle->m_userlist->users[usercnt].name, p->pw_name, sizeof(handle->m_userlist->users[usercnt].name)); } else { *handle->m_userlist->users[usercnt].name = '\0'; } if(p->pw_dir) { strncpy(handle->m_userlist->users[usercnt].homedir, p->pw_dir, sizeof(handle->m_userlist->users[usercnt].homedir)); } else { *handle->m_userlist->users[usercnt].homedir = '\0'; } if(p->pw_shell) { strncpy(handle->m_userlist->users[usercnt].shell, p->pw_shell, sizeof(handle->m_userlist->users[usercnt].shell)); } else { *handle->m_userlist->users[usercnt].shell = '\0'; } handle->m_userlist->totsavelen += sizeof(uint8_t) + // type sizeof(uint32_t) + // uid sizeof(uint32_t) + // gid strlen(handle->m_userlist->users[usercnt].name) + 2 + strlen(handle->m_userlist->users[usercnt].homedir) + 2 + strlen(handle->m_userlist->users[usercnt].shell) + 2; } endpwent(); // groups setgrent(); g = getgrent(); for(grpcnt = 0; g; g = getgrent(), grpcnt++) { handle->m_userlist->groups[grpcnt].gid = g->gr_gid; if(g->gr_name) { strncpy(handle->m_userlist->groups[grpcnt].name, g->gr_name, sizeof(handle->m_userlist->groups[grpcnt].name)); } else { *handle->m_userlist->groups[grpcnt].name = '\0'; } handle->m_userlist->totsavelen += sizeof(uint8_t) + // type sizeof(uint32_t) + // gid strlen(handle->m_userlist->groups[grpcnt].name) + 2; } endgrent(); return SCAP_SUCCESS; } #endif // HAS_CAPTURE // // Free a previously allocated list of users // void scap_free_userlist(scap_userlist* uhandle) { if(uhandle) { free(uhandle->users); free(uhandle->groups); free(uhandle); } } sysdig-0.26.4/userspace/libscap/settings.h000066400000000000000000000015041352731327100205310ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // This flag can be used to include unsupported or unrecognized sockets // in the fd tables. It's useful to debug close() leaks // #define INCLUDE_UNKNOWN_SOCKET_FDS #define USE_ZLIB #define SCAP_NODRIVER_MAX_FD_LOOKUP 20 sysdig-0.26.4/userspace/libscap/stdint_win.h000066400000000000000000000170601352731327100210570ustar00rootroot00000000000000// ISO C9x compliant stdint.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006-2008 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ #error "Use this header only with Microsoft Visual C++ compilers!" #endif // _MSC_VER ] #ifndef _MSC_STDINT_H_ // [ #define _MSC_STDINT_H_ #if _MSC_VER > 1000 #pragma once #endif #include // For Visual Studio 6 in C++ mode and for many Visual Studio versions when // compiling for ARM we should wrap include with 'extern "C++" {}' // or compiler give many errors like this: // error C2733: second C linkage of overloaded function 'wmemchr' not allowed #ifdef __cplusplus extern "C" { #endif # include #ifdef __cplusplus } #endif // Define _W64 macros to mark types changing their size, like intptr_t. #ifndef _W64 # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 # define _W64 __w64 # else # define _W64 # endif #endif // 7.18.1 Integer types // 7.18.1.1 Exact-width integer types // Visual Studio 6 and Embedded Visual C++ 4 doesn't // realize that, e.g. char has the same size as __int8 // so we give up on __intX for them. #if (_MSC_VER < 1300) typedef signed char int8_t; typedef signed short int16_t; typedef signed int int32_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else typedef signed __int8 int8_t; typedef signed __int16 int16_t; typedef signed __int32 int32_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; // 7.18.1.2 Minimum-width integer types typedef int8_t int_least8_t; typedef int16_t int_least16_t; typedef int32_t int_least32_t; typedef int64_t int_least64_t; typedef uint8_t uint_least8_t; typedef uint16_t uint_least16_t; typedef uint32_t uint_least32_t; typedef uint64_t uint_least64_t; // 7.18.1.3 Fastest minimum-width integer types typedef int8_t int_fast8_t; typedef int16_t int_fast16_t; typedef int32_t int_fast32_t; typedef int64_t int_fast64_t; typedef uint8_t uint_fast8_t; typedef uint16_t uint_fast16_t; typedef uint32_t uint_fast32_t; typedef uint64_t uint_fast64_t; // 7.18.1.4 Integer types capable of holding object pointers #ifdef _WIN64 // [ typedef signed __int64 intptr_t; typedef unsigned __int64 uintptr_t; #else // _WIN64 ][ typedef _W64 signed int intptr_t; typedef _W64 unsigned int uintptr_t; #endif // _WIN64 ] // 7.18.1.5 Greatest-width integer types typedef int64_t intmax_t; typedef uint64_t uintmax_t; // 7.18.2 Limits of specified-width integer types #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 // 7.18.2.1 Limits of exact-width integer types #define INT8_MIN ((int8_t)_I8_MIN) #define INT8_MAX _I8_MAX #define INT16_MIN ((int16_t)_I16_MIN) #define INT16_MAX _I16_MAX #define INT32_MIN ((int32_t)_I32_MIN) #define INT32_MAX _I32_MAX #define INT64_MIN ((int64_t)_I64_MIN) #define INT64_MAX _I64_MAX #define UINT8_MAX _UI8_MAX #define UINT16_MAX _UI16_MAX #define UINT32_MAX _UI32_MAX #define UINT64_MAX _UI64_MAX // 7.18.2.2 Limits of minimum-width integer types #define INT_LEAST8_MIN INT8_MIN #define INT_LEAST8_MAX INT8_MAX #define INT_LEAST16_MIN INT16_MIN #define INT_LEAST16_MAX INT16_MAX #define INT_LEAST32_MIN INT32_MIN #define INT_LEAST32_MAX INT32_MAX #define INT_LEAST64_MIN INT64_MIN #define INT_LEAST64_MAX INT64_MAX #define UINT_LEAST8_MAX UINT8_MAX #define UINT_LEAST16_MAX UINT16_MAX #define UINT_LEAST32_MAX UINT32_MAX #define UINT_LEAST64_MAX UINT64_MAX // 7.18.2.3 Limits of fastest minimum-width integer types #define INT_FAST8_MIN INT8_MIN #define INT_FAST8_MAX INT8_MAX #define INT_FAST16_MIN INT16_MIN #define INT_FAST16_MAX INT16_MAX #define INT_FAST32_MIN INT32_MIN #define INT_FAST32_MAX INT32_MAX #define INT_FAST64_MIN INT64_MIN #define INT_FAST64_MAX INT64_MAX #define UINT_FAST8_MAX UINT8_MAX #define UINT_FAST16_MAX UINT16_MAX #define UINT_FAST32_MAX UINT32_MAX #define UINT_FAST64_MAX UINT64_MAX // 7.18.2.4 Limits of integer types capable of holding object pointers #ifdef _WIN64 // [ # define INTPTR_MIN INT64_MIN # define INTPTR_MAX INT64_MAX # define UINTPTR_MAX UINT64_MAX #else // _WIN64 ][ # define INTPTR_MIN INT32_MIN # define INTPTR_MAX INT32_MAX # define UINTPTR_MAX UINT32_MAX #endif // _WIN64 ] // 7.18.2.5 Limits of greatest-width integer types #define INTMAX_MIN INT64_MIN #define INTMAX_MAX INT64_MAX #define UINTMAX_MAX UINT64_MAX // 7.18.3 Limits of other integer types #ifdef _WIN64 // [ # define PTRDIFF_MIN _I64_MIN # define PTRDIFF_MAX _I64_MAX #else // _WIN64 ][ # define PTRDIFF_MIN _I32_MIN # define PTRDIFF_MAX _I32_MAX #endif // _WIN64 ] #define SIG_ATOMIC_MIN INT_MIN #define SIG_ATOMIC_MAX INT_MAX #ifndef SIZE_MAX // [ # ifdef _WIN64 // [ # define SIZE_MAX _UI64_MAX # else // _WIN64 ][ # define SIZE_MAX _UI32_MAX # endif // _WIN64 ] #endif // SIZE_MAX ] // WCHAR_MIN and WCHAR_MAX are also defined in #ifndef WCHAR_MIN // [ # define WCHAR_MIN 0 #endif // WCHAR_MIN ] #ifndef WCHAR_MAX // [ # define WCHAR_MAX _UI16_MAX #endif // WCHAR_MAX ] #define WINT_MIN 0 #define WINT_MAX _UI16_MAX #endif // __STDC_LIMIT_MACROS ] // 7.18.4 Limits of other integer types #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 // 7.18.4.1 Macros for minimum-width integer constants #define INT8_C(val) val##i8 #define INT16_C(val) val##i16 #define INT32_C(val) val##i32 #define INT64_C(val) val##i64 #define UINT8_C(val) val##ui8 #define UINT16_C(val) val##ui16 #define UINT32_C(val) val##ui32 #define UINT64_C(val) val##ui64 // 7.18.4.2 Macros for greatest-width integer constants #define INTMAX_C INT64_C #define UINTMAX_C UINT64_C #endif // __STDC_CONSTANT_MACROS ] #endif // _MSC_STDINT_H_ ] sysdig-0.26.4/userspace/libscap/syscall_info_table.c000066400000000000000000001000661352731327100225230ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "../common/sysdig_types.h" #include "../../driver/ppm_events_public.h" #include "scap.h" /* * SYSCALL INFO TABLE */ const struct ppm_syscall_desc g_syscall_info_table[PPM_SC_MAX] = { /*dummy*/ { EC_OTHER, (enum ppm_event_flags)(EF_NONE), "" }, /*PPM_SC_RESTART_SYSCALL*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "restart_syscall" }, /*PPM_SC_EXIT*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "exit" }, /*PPM_SC_READ*/ { EC_IO_READ, (enum ppm_event_flags)(EF_DROP_FALCO), "read" }, /*PPM_SC_WRITE*/ { EC_IO_WRITE, (enum ppm_event_flags)(EF_DROP_FALCO), "write" }, /*PPM_SC_OPEN*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "open" }, /*PPM_SC_CLOSE*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "close" }, /*PPM_SC_CREAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "creat" }, /*PPM_SC_LINK*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "link" }, /*PPM_SC_UNLINK*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "unlink" }, /*PPM_SC_CHDIR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "chdir" }, /*PPM_SC_TIME*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "time" }, /*PPM_SC_MKNOD*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "mknod" }, /*PPM_SC_CHMOD*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "chmod" }, /*PPM_SC_STAT*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "stat" }, /*PPM_SC_LSEEK*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "lseek" }, /*PPM_SC_GETPID*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "getpid" }, /*PPM_SC_MOUNT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "mount" }, /*PPM_SC_PTRACE*/ { EC_OTHER, (enum ppm_event_flags)(EF_NONE), "ptrace" }, /*PPM_SC_ALARM*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "alarm" }, /*PPM_SC_FSTAT*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "fstat" }, /*PPM_SC_PAUSE*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "pause" }, /* WAIT UNTIL A SIGNAL ARRIVES */ /*PPM_SC_UTIME*/ { EC_TIME, (enum ppm_event_flags)(EF_NONE), "utime" }, /*PPM_SC_ACCESS*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "access" }, /* checks whether the calling process can access the file pathname */ /*PPM_SC_SYNC*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "sync" }, /* causes all buffered modifications to file metadata and data to be written to the underlying file systems. */ /*PPM_SC_KILL*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "kill" }, /*PPM_SC_RENAME*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "rename" }, /*PPM_SC_MKDIR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "mkdir" }, /*PPM_SC_RMDIR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "rmdir" }, /*PPM_SC_DUP*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "dup" }, /*PPM_SC_PIPE*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "pipe" }, /*PPM_SC_TIMES*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "times" }, /*PPM_SC_BRK*/ { EC_MEMORY, (enum ppm_event_flags)(EF_DROP_FALCO), "brk" }, /*PPM_SC_ACCT*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "acct" }, /*PPM_SC_IOCTL*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "ioctl" }, /*PPM_SC_FCNTL*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "fcntl" }, /*PPM_SC_SETPGID*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "setpgid" }, /*PPM_SC_UMASK*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "umask" }, /* sets the calling process's file mode creation mask */ /*PPM_SC_CHROOT*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "chroot" }, /* changes the root directory of the calling process to that specified in path. This directory will be used for path names beginning with /. The root directory is inherited by all children of the calling process. */ /*PPM_SC_USTAT*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "ustat" }, /* returns information about a mounted file system. */ /*PPM_SC_DUP2*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "dup2" }, /*PPM_SC_GETPPID*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "getppid" }, /*PPM_SC_GETPGRP*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "getpgrp" }, /*PPM_SC_SETSID*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "setsid" }, /* creates a session and sets the process group ID */ /*PPM_SC_SETHOSTNAME*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "sethostname" }, /*PPM_SC_SETRLIMIT*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "setrlimit" }, /* get/set resource (CPU, FDs, memory...) limits */ /*PPM_SC_GETRUSAGE*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "getrusage" }, /* returns resource usage measures for who */ /*PPM_SC_GETTIMEOFDAY*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "gettimeofday" }, /*PPM_SC_SETTIMEOFDAY*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "settimeofday" }, /*PPM_SC_SYMLINK*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "symlink" }, /*PPM_SC_LSTAT*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "lstat" }, /*PPM_SC_READLINK*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "readlink" }, /*PPM_SC_USELIB*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "uselib" }, /* load shared library */ /*PPM_SC_SWAPON*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "swapon" }, /* start/stop swapping to file/device */ /*PPM_SC_REBOOT*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "reboot" }, /*PPM_SC_MMAP*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "mmap" }, /*PPM_SC_MUNMAP*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "munmap" }, /*PPM_SC_TRUNCATE*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "truncate" }, /* truncate a file to a specified length */ /*PPM_SC_FTRUNCATE*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "ftruncate" }, /* truncate a file to a specified length */ /*PPM_SC_FCHMOD*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "fchmod" }, /*PPM_SC_GETPRIORITY*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "getpriority" }, /* get/set program scheduling priority */ /*PPM_SC_SETPRIORITY*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "setpriority" }, /* get/set program scheduling priority */ /*PPM_SC_STATFS*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "statfs" }, /* returns information about a mounted file system */ /*PPM_SC_FSTATFS*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "fstatfs" }, /* returns information about a mounted file system */ /*PPM_SC_SYSLOG*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "syslog" }, /* read and/or clear kernel message ring buffer; set console_loglevel */ /*PPM_SC_SETITIMER*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "setitimer" }, /*PPM_SC_GETITIMER*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "getitimer" }, /*PPM_SC_UNAME*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_DROP_FALCO), "uname" }, /* get name and information about current kernel */ /*PPM_SC_VHANGUP*/ { EC_OTHER , (enum ppm_event_flags)(EF_NONE), "vhangup" }, /* simulates a hangup on the current terminal. This call arranges for other users to have a "clean" terminal at login time. */ /*PPM_SC_WAIT4*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "wait4" }, /* OBSOLETE */ /*PPM_SC_SWAPOFF*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "swapoff" }, /* start/stop swapping to file/device */ /*PPM_SC_SYSINFO*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "sysinfo" }, /* returns information on overall system statistics */ /*PPM_SC_FSYNC*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "fsync" }, /* sync file content */ /*PPM_SC_SETDOMAINNAME*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "setdomainname" }, /*PPM_SC_ADJTIMEX*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "adjtimex" }, /* tune kernel clock */ /*PPM_SC_MPROTECT*/ { EC_MEMORY, (enum ppm_event_flags)(EF_DROP_FALCO), "mprotect" }, /* set protection on a region of memory */ /*PPM_SC_INIT_MODULE*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "init_module" }, /* load a kernel module */ /*PPM_SC_DELETE_MODULE*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "delete_module" }, /*PPM_SC_QUOTACTL*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "quotactl" }, /*PPM_SC_GETPGID*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "getpgid" }, /*PPM_SC_FCHDIR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "fchdir" }, /*PPM_SC_SYSFS*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "sysfs" }, /* get file system type information */ /*PPM_SC_PERSONALITY*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "personality" }, /* set the process execution domain */ /*PPM_SC_GETDENTS*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "getdents" }, /* get directory entries */ /*PPM_SC_SELECT*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "select" }, /*PPM_SC_FLOCK*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "flock" }, /* apply or remove an advisory lock on an open file */ /*PPM_SC_MSYNC*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "msync" }, /* synchronize a file with a memory map */ /*PPM_SC_READV*/ { EC_IO_READ, (enum ppm_event_flags)(EF_DROP_FALCO), "readv" }, /*PPM_SC_WRITEV*/ { EC_IO_WRITE, (enum ppm_event_flags)(EF_DROP_FALCO), "writev" }, /*PPM_SC_GETSID*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "getsid" }, /* returns the session ID of the calling process */ /*PPM_SC_FDATASYNC*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "fdatasync" }, /* synchronize a file's in-core state with storage device */ /*PPM_SC_MLOCK*/ { EC_MEMORY, (enum ppm_event_flags)(EF_DROP_FALCO), "mlock" }, /* mlock() and mlockall() respectively lock part or all of the calling process's virtual address space into RAM */ /*PPM_SC_MUNLOCK*/ { EC_MEMORY, (enum ppm_event_flags)(EF_DROP_FALCO), "munlock" }, /* mlock() and mlockall() respectively lock part or all of the calling process's virtual address space into RAM */ /*PPM_SC_MLOCKALL*/ { EC_MEMORY, (enum ppm_event_flags)(EF_DROP_FALCO), "mlockall" }, /* mlock() and mlockall() respectively lock part or all of the calling process's virtual address space into RAM */ /*PPM_SC_MUNLOCKALL*/ { EC_MEMORY, (enum ppm_event_flags)(EF_DROP_FALCO), "munlockall" }, /* mlock() and mlockall() respectively lock part or all of the calling process's virtual address space into RAM */ /*PPM_SC_SCHED_SETPARAM*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "sched_setparam" }, /*PPM_SC_SCHED_GETPARAM*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "sched_getparam" }, /*PPM_SC_SCHED_SETSCHEDULER*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "sched_setscheduler" }, /*PPM_SC_SCHED_GETSCHEDULER*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "sched_getscheduler" }, /*PPM_SC_SCHED_YIELD*/ { EC_SLEEP, (enum ppm_event_flags)(EF_DROP_FALCO), "sched_yield" }, /*PPM_SC_SCHED_GET_PRIORITY_MAX*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "sched_get_priority_max" }, /*PPM_SC_SCHED_GET_PRIORITY_MIN*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "sched_get_priority_min" }, /*PPM_SC_SCHED_RR_GET_INTERVAL*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "sched_rr_get_interval" }, /*PPM_SC_NANOSLEEP*/ { EC_SLEEP, (enum ppm_event_flags)(EF_DROP_FALCO), "nanosleep" }, /*PPM_SC_MREMAP*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "mremap" }, /*PPM_SC_POLL*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "poll" }, /*PPM_SC_PRCTL*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "prctl" }, /* operations on a process */ /*PPM_SC_RT_SIGACTION*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_DROP_FALCO), "rt_sigaction" }, /*PPM_SC_RT_SIGPROCMASK*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_DROP_FALCO), "rt_sigprocmask" }, /*PPM_SC_RT_SIGPENDING*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_DROP_FALCO), "rt_sigpending" }, /*PPM_SC_RT_SIGTIMEDWAIT*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_DROP_FALCO), "rt_sigtimedwait" }, /*PPM_SC_RT_SIGQUEUEINFO*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_NONE), "rt_sigqueueinfo" }, /*PPM_SC_RT_SIGSUSPEND*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_DROP_FALCO), "rt_sigsuspend" }, /*PPM_SC_GETCWD*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "getcwd" }, /*PPM_SC_CAPGET*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "capget" }, /* set/get capabilities of thread(s) */ /*PPM_SC_CAPSET*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "capset" }, /* set/get capabilities of thread(s) */ /*PPM_SC_SENDFILE*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "sendfile" }, /* transfer data between file descriptors */ /*PPM_SC_GETRLIMIT*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "getrlimit" }, /*PPM_SC_LCHOWN*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "lchown" }, /*PPM_SC_GETUID*/ { EC_USER, (enum ppm_event_flags)(EF_DROP_FALCO), "getuid" }, /*PPM_SC_GETGID*/ { EC_USER, (enum ppm_event_flags)(EF_DROP_FALCO), "getgid" }, /*PPM_SC_GETEUID*/ { EC_USER, (enum ppm_event_flags)(EF_DROP_FALCO), "geteuid" }, /*PPM_SC_GETEGID*/ { EC_USER, (enum ppm_event_flags)(EF_DROP_FALCO), "getegid" }, /*PPM_SC_SETREUID*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "setreuid" }, /*PPM_SC_SETREGID*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "setregid" }, /*PPM_SC_GETGROUPS*/ { EC_USER, (enum ppm_event_flags)(EF_DROP_FALCO), "getgroups" }, /* returns the supplementary group IDs of the calling process */ /*PPM_SC_SETGROUPS*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "setgroups" }, /* returns the supplementary group IDs of the calling process */ /*PPM_SC_FCHOWN*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "fchown" }, /*PPM_SC_SETRESUID*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "setresuid" }, /*PPM_SC_GETRESUID*/ { EC_USER, (enum ppm_event_flags)(EF_DROP_FALCO), "getresuid" }, /*PPM_SC_SETRESGID*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "setresgid" }, /*PPM_SC_GETRESGID*/ { EC_USER, (enum ppm_event_flags)(EF_DROP_FALCO), "getresgid" }, /*PPM_SC_CHOWN*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "chown" }, /*PPM_SC_SETUID*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "setuid" }, /*PPM_SC_SETGID*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "setgid" }, /*PPM_SC_SETFSUID*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "setfsuid" }, /*PPM_SC_SETFSGID*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "setfsgid" }, /*PPM_SC_PIVOT_ROOT*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "pivot_root" }, /*PPM_SC_MINCORE*/ { EC_MEMORY, (enum ppm_event_flags)(EF_DROP_FALCO), "mincore" }, /* determine whether pages are resident in memory */ /*PPM_SC_MADVISE*/ { EC_MEMORY, (enum ppm_event_flags)(EF_DROP_FALCO), "madvise" }, /* give advice about use of memory */ /*PPM_SC_GETTID*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "gettid" }, /* returns the caller's thread ID (TID) */ /*PPM_SC_SETXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "setxattr" }, /* set inode attribute */ /*PPM_SC_LSETXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "lsetxattr" }, /*PPM_SC_FSETXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "fsetxattr" }, /*PPM_SC_GETXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "getxattr" }, /*PPM_SC_LGETXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "lgetxattr" }, /*PPM_SC_FGETXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "fgetxattr" }, /*PPM_SC_LISTXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "listxattr" }, /*PPM_SC_LLISTXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "llistxattr" }, /*PPM_SC_FLISTXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "flistxattr" }, /*PPM_SC_REMOVEXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "removexattr" }, /*PPM_SC_LREMOVEXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "lremovexattr" }, /*PPM_SC_FREMOVEXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "fremovexattr" }, /*PPM_SC_TKILL*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_NONE), "tkill" }, /* send a signal to a thread */ /*PPM_SC_FUTEX*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "futex" }, /*PPM_SC_SCHED_SETAFFINITY*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "sched_setaffinity" }, /*PPM_SC_SCHED_GETAFFINITY*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "sched_getaffinity" }, /*PPM_SC_SET_THREAD_AREA*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "set_thread_area" }, /*PPM_SC_GET_THREAD_AREA*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "get_thread_area" }, /*PPM_SC_IO_SETUP*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "io_setup" }, /* create an asynchronous I/O context (for libaio) */ /*PPM_SC_IO_DESTROY*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "io_destroy" }, /*PPM_SC_IO_GETEVENTS*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "io_getevents" }, /*PPM_SC_IO_SUBMIT*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "io_submit" }, /*PPM_SC_IO_CANCEL*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "io_cancel" }, /*PPM_SC_EXIT_GROUP*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "exit_group" }, /*PPM_SC_EPOLL_CREATE*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "epoll_create" }, /*PPM_SC_EPOLL_CTL*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "epoll_ctl" }, /*PPM_SC_EPOLL_WAIT*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "epoll_wait" }, /*PPM_SC_REMAP_FILE_PAGES*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "remap_file_pages" }, /* create a nonlinear file mapping */ /*PPM_SC_SET_TID_ADDRESS*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "set_tid_address" }, /* set pointer to thread ID */ /*PPM_SC_TIMER_CREATE*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "timer_create" }, /*PPM_SC_TIMER_SETTIME*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "timer_settime" }, /*PPM_SC_TIMER_GETTIME*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "timer_gettime" }, /*PPM_SC_TIMER_GETOVERRUN*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "timer_getoverrun" }, /*PPM_SC_TIMER_DELETE*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "timer_delete" }, /*PPM_SC_CLOCK_SETTIME*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "clock_settime" }, /*PPM_SC_CLOCK_GETTIME*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "clock_gettime" }, /*PPM_SC_CLOCK_GETRES*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "clock_getres" }, /*PPM_SC_CLOCK_NANOSLEEP*/ { EC_SLEEP, (enum ppm_event_flags)(EF_DROP_FALCO), "clock_nanosleep" }, /*PPM_SC_TGKILL*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_NONE), "tgkill" }, /*PPM_SC_UTIMES*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "utimes" }, /* change file last access and modification times */ /*PPM_SC_MQ_OPEN*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "mq_open" }, /* Message queues. See http://linux.die.net/man/7/mq_overview. */ /*PPM_SC_MQ_UNLINK*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "mq_unlink" }, /*PPM_SC_MQ_TIMEDSEND*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "mq_timedsend" }, /*PPM_SC_MQ_TIMEDRECEIVE*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "mq_timedreceive" }, /*PPM_SC_MQ_NOTIFY*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "mq_notify" }, /*PPM_SC_MQ_GETSETATTR*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "mq_getsetattr" }, /*PPM_SC_KEXEC_LOAD*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "kexec_load" }, /* load a new kernel for later execution */ /*PPM_SC_WAITID*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "waitid" }, /*PPM_SC_ADD_KEY*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "add_key" }, /* add a key to the kernel's key management facility */ /*PPM_SC_REQUEST_KEY*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "request_key" }, /*PPM_SC_KEYCTL*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "keyctl" }, /*PPM_SC_IOPRIO_SET*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "ioprio_set" }, /* get/set I/O scheduling class and priority */ /*PPM_SC_IOPRIO_GET*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "ioprio_get" }, /* get/set I/O scheduling class and priority */ /*PPM_SC_INOTIFY_INIT*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "inotify_init" }, /* initialize an inotify event queue instance. See http://en.wikipedia.org/wiki/Inotify. */ /*PPM_SC_INOTIFY_ADD_WATCH*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "inotify_add_watch" }, /*PPM_SC_INOTIFY_RM_WATCH*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "inotify_rm_watch" }, /*PPM_SC_OPENAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "openat" }, /*PPM_SC_MKDIRAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "mkdirat" }, /*PPM_SC_MKNODAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "mknodat" }, /*PPM_SC_FCHOWNAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "fchownat" }, /*PPM_SC_FUTIMESAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "futimesat" }, /*PPM_SC_UNLINKAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "unlinkat" }, /*PPM_SC_RENAMEAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "renameat" }, /*PPM_SC_LINKAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "linkat" }, /*PPM_SC_SYMLINKAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "symlinkat" }, /*PPM_SC_READLINKAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "readlinkat" }, /*PPM_SC_FCHMODAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "fchmodat" }, /*PPM_SC_FACCESSAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "faccessat" }, /*PPM_SC_PSELECT6*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "pselect6" }, /*PPM_SC_PPOLL*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "ppoll" }, /*PPM_SC_UNSHARE*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "unshare" }, /* disassociate parts of the process execution context */ /*PPM_SC_SET_ROBUST_LIST*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "set_robust_list" }, /* get/set list of robust futexes */ /*PPM_SC_GET_ROBUST_LIST*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "get_robust_list" }, /* get/set list of robust futexes */ /*PPM_SC_SPLICE*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "splice" }, /* transfers up to len bytes of data from the file descriptor fd_in to the file descriptor fd_out, where one of the descriptors must refer to a pipe. */ /*PPM_SC_TEE*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "tee" }, /* tee() duplicates up to len bytes of data from the pipe referred to by the file descriptor fd_in to the pipe referred to by the file descriptor fd_out. It does not consume the data that is duplicated from fd_in. */ /*PPM_SC_VMSPLICE*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "vmsplice" }, /* splice user pages into a pipe */ /*PPM_SC_GETCPU*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "getcpu" }, /* determine CPU and NUMA node on which the calling thread is running */ /*PPM_SC_EPOLL_PWAIT*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "epoll_pwait" }, /*PPM_SC_UTIMENSAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "utimensat" }, /* change file timestamps with nanosecond precision */ /*PPM_SC_SIGNALFD*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_DROP_FALCO), "signalfd" }, /* create a pollable file descriptor for accepting signals */ /*PPM_SC_TIMERFD_CREATE*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "timerfd_create" }, /* // create and operate on a timer that delivers timer expiration notifications via a file descriptor */ /*PPM_SC_EVENTFD*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "eventfd" }, /* create a file descriptor for event notification */ /*PPM_SC_TIMERFD_SETTIME*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "timerfd_settime" }, /* create and operate on a timer that delivers timer expiration notifications via a file descriptor */ /*PPM_SC_TIMERFD_GETTIME*/ { EC_TIME, (enum ppm_event_flags)(EF_DROP_FALCO), "timerfd_gettime" }, /* create and operate on a timer that delivers timer expiration notifications via a file descriptor */ /*PPM_SC_SIGNALFD4*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_DROP_FALCO), "signalfd4" }, /* create a pollable file descriptor for accepting signals */ /*PPM_SC_EVENTFD2*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "eventfd2" }, /* create a file descriptor for event notification */ /*PPM_SC_EPOLL_CREATE1*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "epoll_create1" }, /* variant of epoll_create */ /*PPM_SC_DUP3*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "dup3" }, /*PPM_SC_PIPE2*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "pipe2" }, /*PPM_SC_INOTIFY_INIT1*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "inotify_init1" }, /*PPM_SC_PREADV*/ { EC_IO_READ, (enum ppm_event_flags)(EF_DROP_FALCO), "preadv" }, /*PPM_SC_PWRITEV*/ { EC_IO_WRITE, (enum ppm_event_flags)(EF_DROP_FALCO), "pwritev" }, /*PPM_SC_RT_TGSIGQUEUEINFO*/ { EC_OTHER, (enum ppm_event_flags)(EF_NONE), "rt_tgsigqueueinfo" }, /*PPM_SC_PERF_EVENT_OPEN*/ { EC_OTHER, (enum ppm_event_flags)(EF_NONE), "perf_event_open" }, /*PPM_SC_FANOTIFY_INIT*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "fanotify_init" }, /*PPM_SC_PRLIMIT64*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "prlimit64" }, /*PPM_SC_CLOCK_ADJTIME*/ { EC_OTHER, (enum ppm_event_flags)(EF_NONE), "clock_adjtime" }, /*PPM_SC_SYNCFS*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "syncfs" }, /*PPM_SC_SETNS*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "setns" }, /* reassociate thread with a namespace */ /*PPM_SC_GETDENTS64*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "getdents64" }, /* */ /* Non-multiplexed socket family */ /* */ /*PPM_SC_SOCKET*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "socket" }, /*PPM_SC_BIND*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "bind" }, /*PPM_SC_CONNECT*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "connect" }, /*PPM_SC_LISTEN*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "listen" }, /*PPM_SC_ACCEPT*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "accept" }, /*PPM_SC_GETSOCKNAME*/ { EC_NET, (enum ppm_event_flags)(EF_DROP_FALCO), "getsockname" }, /*PPM_SC_GETPEERNAME*/ { EC_NET, (enum ppm_event_flags)(EF_DROP_FALCO), "getpeername" }, /*PPM_SC_SOCKETPAIR*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "socketpair" }, /*PPM_SC_SENDTO*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "sendto" }, /*PPM_SC_RECVFROM*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "recvfrom" }, /*PPM_SC_SHUTDOWN*/ { EC_NET, (enum ppm_event_flags)(EF_DROP_FALCO), "shutdown" }, /*PPM_SC_SETSOCKOPT*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "setsockopt" }, /*PPM_SC_GETSOCKOPT*/ { EC_NET, (enum ppm_event_flags)(EF_DROP_FALCO), "getsockopt" }, /*PPM_SC_SENDMSG*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "sendmsg" }, /*PPM_SC_SENDMMSG*/ { EC_NET, (enum ppm_event_flags)(EF_DROP_FALCO), "sendmmsg" }, /*PPM_SC_RECVMSG*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "recvmsg" }, /*PPM_SC_RECVMMSG*/ { EC_NET, (enum ppm_event_flags)(EF_DROP_FALCO), "recvmmsg" }, /*PPM_SC_ACCEPT4*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "accept4" }, /* * Non-multiplexed IPC family */ /*PPM_SC_SEMOP*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "semop" }, /*PPM_SC_SEMGET*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "semget" }, /*PPM_SC_SEMCTL*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "semctl" }, /*PPM_SC_MSGSND*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "msgsnd" }, /*PPM_SC_MSGRCV*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "msgrcv" }, /*PPM_SC_MSGGET*/ { EC_IPC, (enum ppm_event_flags)(EF_DROP_FALCO), "msgget" }, /*PPM_SC_MSGCTL*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "msgctl" }, /*PPM_SC_SHMDT*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "shmdt" }, /*PPM_SC_SHMGET*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "shmget" }, /*PPM_SC_SHMCTL*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "shmctl" }, /*PPM_SC_STATFS64*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "statfs64" }, /*PPM_SC_FSTATFS64*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "fstatfs64" }, /*PPM_SC_FSTATAT64*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "fstatat64" }, /*PPM_SC_SENDFILE64*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "sendfile64" }, /*PPM_SC_UGETRLIMIT*/ { EC_PROCESS, (enum ppm_event_flags)(EF_DROP_FALCO), "ugetrlimit" }, /*PPM_SC_BDFLUSH*/ { EC_OTHER, (enum ppm_event_flags)(EF_NONE), "bdflush" }, /* deprecated */ /*PPM_SC_SIGPROCMASK*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_DROP_FALCO), "sigprocmask" }, /* examine and change blocked signals */ /*PPM_SC_IPC*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "ipc" }, /*PPM_SC_SOCKETCALL*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "socketcall" }, /*PPM_SC_STAT64*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "stat64" }, /*PPM_SC_LSTAT64*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "lstat64" }, /*PPM_SC_FSTAT64*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "fstat64" }, /*PPM_SC_FCNTL64*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "fcntl64" }, /*PPM_SC_MMAP2*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "mmap2" }, /*PPM_SC__NEWSELECT*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "newselect" }, /*PPM_SC_SGETMASK*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_DROP_FALCO), "sgetmask" }, /* manipulation of signal mask (obsolete) */ /*PPM_SC_SSETMASK*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_NONE), "ssetmask" }, /* manipulation of signal mask (obsolete) */ /*PPM_SC_SIGPENDING*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_DROP_FALCO), "sigpending" }, /* examine pending signals */ /*PPM_SC_OLDUNAME*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_DROP_FALCO), "olduname" }, /*PPM_SC_UMOUNT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "umount" }, /*PPM_SC_SIGNAL*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_NONE), "signal" }, /*PPM_SC_NICE*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "nice" }, /* change process priority */ /*PPM_SC_STIME*/ { EC_TIME, (enum ppm_event_flags)(EF_NONE), "stime" }, /*PPM_SC__LLSEEK*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "llseek" }, /*PPM_SC_WAITPID*/ { EC_WAIT, (enum ppm_event_flags)(EF_DROP_FALCO), "waitpid" }, /*PPM_SC_PREAD64*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "pread64" }, /*PPM_SC_PWRITE64*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "pwrite64" }, /*PPM_SC_ARCH_PRCTL*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "arch_prctl" }, /*PPM_SC_SHMAT*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "shmat" }, /*PPM_SC_SIGRETURN*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_DROP_FALCO), "sigreturn" }, /* return from signal handler and cleanup stack frame */ /*PPM_SC_FALLOCATE*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "fallocate" }, /* manipulate file space */ /*PPM_SC_NEWFSSTAT*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "newfstatat" }, /*PPM_SC_PROCESS_VM_READV*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "process_vm_readv" }, /*PPM_SC_PROCESS_VM_WRITEV*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "process_vm_writev" }, /*PPM_SC_FORK*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "fork" }, /*PPM_SC_VFORK*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "vfork" }, /*PPM_SC_SETUID32*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "setuid" }, /*PPM_SC_GETUID32*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "getuid" }, /*PPM_SC_SETGID32*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "setgid" }, /*PPM_SC_GETEUID32*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "geteuid" }, /*PPM_SC_GETGID32*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "getgid" }, /*PPM_SC_SETRESUID32*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "setresuid" }, /*PPM_SC_SETRESGID32*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "setresgid" }, /*PPM_SC_GETRESUID32*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "getresuid" }, /*PPM_SC_GETRESGID32*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_DROP_FALCO), "getresgid" }, /*PPM_SC_FINIT_MODULE*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "finit_module" }, /* load a kernel module */ /*PPM_SC_BPF*/ { EC_OTHER, (enum ppm_event_flags)(EF_NONE), "bpf" }, /*PPM_SC_SECCOMP*/ { EC_OTHER, (enum ppm_event_flags)(EF_NONE), "seccomp" }, /*PPM_SC_SIGALTSTACK*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "sigaltstack" }, /*PPM_SC_GETRANDOM*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "getrandom" }, /*PPM_SC_FADVISE64*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "fadvise64" }, }; bool validate_info_table_size() { return (sizeof(g_syscall_info_table) / sizeof(g_syscall_info_table[0]) == PPM_SC_MAX); } sysdig-0.26.4/userspace/libscap/uthash.h000066400000000000000000001643651352731327100202040ustar00rootroot00000000000000/* Copyright (c) 2003-2013, Troy D. Hanson http://uthash.sourceforge.net All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef UTHASH_H #define UTHASH_H #include /* memcmp,strlen */ #include /* ptrdiff_t */ #include /* exit() */ /* These macros use decltype or the earlier __typeof GNU extension. As decltype is only available in newer compilers (VS2010 or gcc 4.3+ when compiling c++ source) this code uses whatever method is needed or, for VS2008 where neither is available, uses casting workarounds. */ #ifdef _MSC_VER /* MS compiler */ #if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ #define DECLTYPE(x) (decltype(x)) #else /* VS2008 or older (or VS2010 in C mode) */ #define NO_DECLTYPE #define DECLTYPE(x) #endif #else /* GNU, Sun and other compilers */ #define DECLTYPE(x) (__typeof(x)) #endif #ifdef NO_DECLTYPE #define DECLTYPE_ASSIGN(dst,src) \ do { \ char **_da_dst = (char**)(&(dst)); \ *_da_dst = (char*)(src); \ } while(0) #else #define DECLTYPE_ASSIGN(dst,src) \ do { \ (dst) = DECLTYPE(dst)(src); \ } while(0) #endif /* a number of the hash function use uint32_t which isn't defined on win32 */ #ifdef _MSC_VER typedef unsigned int uint32_t; typedef unsigned char uint8_t; #else #include /* uint32_t */ #endif #define UTHASH_VERSION 1.9.7 #ifndef uthash_fatal #define uthash_fatal(msg) uth_status = SCAP_FAILURE /* fatal error (out of memory,etc) */ #endif #ifndef uthash_malloc #define uthash_malloc(sz) malloc(sz) /* malloc fcn */ #endif #ifndef uthash_free #define uthash_free(ptr,sz) free(ptr) /* free fcn */ #endif #ifndef uthash_noexpand_fyi #define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ #endif #ifndef uthash_expand_fyi #define uthash_expand_fyi(tbl) /* can be defined to log expands */ #endif /* initial number of buckets */ #define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ #define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ #define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ /* calculate the element whose hash handle address is hhe */ #define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) #define HASH_FIND(hh,head,keyptr,keylen,out) \ do { \ unsigned _hf_bkt,_hf_hashv; \ out=NULL; \ if (head) { \ HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ keyptr,keylen,out); \ } \ } \ } while (0) #ifdef HASH_BLOOM #define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) #define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) #define HASH_BLOOM_MAKE(tbl) \ do { \ (tbl)->bloom_nbits = HASH_BLOOM; \ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ } while (0) #define HASH_BLOOM_FREE(tbl) \ do { \ uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ } while (0) #define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) #define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) #define HASH_BLOOM_ADD(tbl,hashv) \ HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) #define HASH_BLOOM_TEST(tbl,hashv) \ HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) #else #define HASH_BLOOM_MAKE(tbl) #define HASH_BLOOM_FREE(tbl) #define HASH_BLOOM_ADD(tbl,hashv) #define HASH_BLOOM_TEST(tbl,hashv) (1) #endif #define HASH_MAKE_TABLE(hh,head) \ do { \ (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ sizeof(UT_hash_table)); \ if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ (head)->hh.tbl->tail = &((head)->hh); \ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ memset((head)->hh.tbl->buckets, 0, \ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ HASH_BLOOM_MAKE((head)->hh.tbl); \ (head)->hh.tbl->signature = HASH_SIGNATURE; \ } while(0) #define HASH_ADD(hh,head,fieldname,keylen_in,add) \ HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) #define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ do { \ unsigned _ha_bkt; \ (add)->hh.next = NULL; \ (add)->hh.key = (char*)keyptr; \ (add)->hh.keylen = (unsigned)keylen_in; \ if (!(head)) { \ head = (add); \ (head)->hh.prev = NULL; \ HASH_MAKE_TABLE(hh,head); \ } else { \ (head)->hh.tbl->tail->next = (add); \ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ (head)->hh.tbl->tail = &((add)->hh); \ } \ (head)->hh.tbl->num_items++; \ (add)->hh.tbl = (head)->hh.tbl; \ HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ (add)->hh.hashv, _ha_bkt); \ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ HASH_FSCK(hh,head); \ } while(0) #define HASH_TO_BKT( hashv, num_bkts, bkt ) \ do { \ bkt = ((hashv) & ((num_bkts) - 1)); \ } while(0) /* delete "delptr" from the hash table. * "the usual" patch-up process for the app-order doubly-linked-list. * The use of _hd_hh_del below deserves special explanation. * These used to be expressed using (delptr) but that led to a bug * if someone used the same symbol for the head and deleted, like * HASH_DELETE(hh,users,users); * We want that to work, but by changing the head (users) below * we were forfeiting our ability to further refer to the deleted (users) * in the patch-up process. Solution: use scratch space to * copy the deleted pointer, then the latter references are via that * scratch pointer rather than through the repointed (users) symbol. */ #define HASH_DELETE(hh,head,delptr) \ do { \ unsigned _hd_bkt; \ struct UT_hash_handle *_hd_hh_del; \ if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ uthash_free((head)->hh.tbl->buckets, \ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ HASH_BLOOM_FREE((head)->hh.tbl); \ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ head = NULL; \ } else { \ _hd_hh_del = &((delptr)->hh); \ if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ (head)->hh.tbl->tail = \ (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ (head)->hh.tbl->hho); \ } \ if ((delptr)->hh.prev) { \ ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ } else { \ DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ } \ if (_hd_hh_del->next) { \ ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ (head)->hh.tbl->hho))->prev = \ _hd_hh_del->prev; \ } \ HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ (head)->hh.tbl->num_items--; \ } \ HASH_FSCK(hh,head); \ } while (0) /* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ #define HASH_FIND_STR(head,findstr,out) \ HASH_FIND(hh,head,findstr,strlen(findstr),out) #define HASH_ADD_STR(head,strfield,add) \ HASH_ADD(hh,head,strfield,strlen(add->strfield),add) #define HASH_FIND_INT(head,findint,out) \ HASH_FIND(hh,head,findint,sizeof(int),out) #define HASH_ADD_INT(head,intfield,add) \ HASH_ADD(hh,head,intfield,sizeof(int),add) #define HASH_FIND_INT32(head,findint,out) \ HASH_FIND(hh,head,findint,sizeof(uint32_t),out) #define HASH_ADD_INT32(head,intfield,add) \ HASH_ADD(hh,head,intfield,sizeof(uint32_t),add) #define HASH_FIND_INT64(head,findint,out) \ HASH_FIND(hh,head,findint,sizeof(uint64_t),out) #define HASH_ADD_INT64(head,intfield,add) \ HASH_ADD(hh,head,intfield,sizeof(uint64_t),add) #define HASH_FIND_PTR(head,findptr,out) \ HASH_FIND(hh,head,findptr,sizeof(void *),out) #define HASH_ADD_PTR(head,ptrfield,add) \ HASH_ADD(hh,head,ptrfield,sizeof(void *),add) #define HASH_DEL(head,delptr) \ HASH_DELETE(hh,head,delptr) /* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. */ #ifdef HASH_DEBUG #define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) #define HASH_FSCK(hh,head) \ do { \ unsigned _bkt_i; \ unsigned _count, _bkt_count; \ char *_prev; \ struct UT_hash_handle *_thh; \ if (head) { \ _count = 0; \ for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ _bkt_count = 0; \ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ _prev = NULL; \ while (_thh) { \ if (_prev != (char*)(_thh->hh_prev)) { \ HASH_OOPS("invalid hh_prev %p, actual %p\n", \ _thh->hh_prev, _prev ); \ } \ _bkt_count++; \ _prev = (char*)(_thh); \ _thh = _thh->hh_next; \ } \ _count += _bkt_count; \ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ HASH_OOPS("invalid bucket count %d, actual %d\n", \ (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ } \ } \ if (_count != (head)->hh.tbl->num_items) { \ HASH_OOPS("invalid hh item count %d, actual %d\n", \ (head)->hh.tbl->num_items, _count ); \ } \ /* traverse hh in app order; check next/prev integrity, count */ \ _count = 0; \ _prev = NULL; \ _thh = &(head)->hh; \ while (_thh) { \ _count++; \ if (_prev !=(char*)(_thh->prev)) { \ HASH_OOPS("invalid prev %p, actual %p\n", \ _thh->prev, _prev ); \ } \ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ (head)->hh.tbl->hho) : NULL ); \ } \ if (_count != (head)->hh.tbl->num_items) { \ HASH_OOPS("invalid app item count %d, actual %d\n", \ (head)->hh.tbl->num_items, _count ); \ } \ } \ } while (0) #else #define HASH_FSCK(hh,head) #endif /* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to * the descriptor to which this macro is defined for tuning the hash function. * The app can #include to get the prototype for write(2). */ #ifdef HASH_EMIT_KEYS #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ do { \ unsigned _klen = fieldlen; \ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ write(HASH_EMIT_KEYS, keyptr, fieldlen); \ } while (0) #else #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) #endif /* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ #ifdef HASH_FUNCTION #define HASH_FCN HASH_FUNCTION #else #define HASH_FCN HASH_JEN #endif /* The Bernstein hash function, used in Perl prior to v5.6 */ #define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _hb_keylen=keylen; \ char *_hb_key=(char*)(key); \ (hashv) = 0; \ while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \ bkt = (hashv) & (num_bkts-1); \ } while (0) /* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ #define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _sx_i; \ char *_hs_key=(char*)(key); \ hashv = 0; \ for(_sx_i=0; _sx_i < keylen; _sx_i++) \ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ bkt = hashv & (num_bkts-1); \ } while (0) #define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _fn_i; \ char *_hf_key=(char*)(key); \ hashv = 2166136261UL; \ for(_fn_i=0; _fn_i < keylen; _fn_i++) \ hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ bkt = hashv & (num_bkts-1); \ } while(0) #define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _ho_i; \ char *_ho_key=(char*)(key); \ hashv = 0; \ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ hashv += _ho_key[_ho_i]; \ hashv += (hashv << 10); \ hashv ^= (hashv >> 6); \ } \ hashv += (hashv << 3); \ hashv ^= (hashv >> 11); \ hashv += (hashv << 15); \ bkt = hashv & (num_bkts-1); \ } while(0) #define HASH_JEN_MIX(a,b,c) \ do { \ a -= b; a -= c; a ^= ( c >> 13 ); \ b -= c; b -= a; b ^= ( a << 8 ); \ c -= a; c -= b; c ^= ( b >> 13 ); \ a -= b; a -= c; a ^= ( c >> 12 ); \ b -= c; b -= a; b ^= ( a << 16 ); \ c -= a; c -= b; c ^= ( b >> 5 ); \ a -= b; a -= c; a ^= ( c >> 3 ); \ b -= c; b -= a; b ^= ( a << 10 ); \ c -= a; c -= b; c ^= ( b >> 15 ); \ } while (0) #define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _hj_i,_hj_j,_hj_k; \ char *_hj_key=(char*)(key); \ hashv = 0xfeedbeef; \ _hj_i = _hj_j = 0x9e3779b9; \ _hj_k = (unsigned)keylen; \ while (_hj_k >= 12) { \ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + ( (unsigned)_hj_key[2] << 16 ) \ + ( (unsigned)_hj_key[3] << 24 ) ); \ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + ( (unsigned)_hj_key[6] << 16 ) \ + ( (unsigned)_hj_key[7] << 24 ) ); \ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + ( (unsigned)_hj_key[10] << 16 ) \ + ( (unsigned)_hj_key[11] << 24 ) ); \ \ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ \ _hj_key += 12; \ _hj_k -= 12; \ } \ hashv += keylen; \ switch ( _hj_k ) { \ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ case 5: _hj_j += _hj_key[4]; \ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ case 1: _hj_i += _hj_key[0]; \ } \ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ bkt = hashv & (num_bkts-1); \ } while(0) /* The Paul Hsieh hash function */ #undef get16bits #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) #define get16bits(d) (*((const uint16_t *) (d))) #endif #if !defined (get16bits) #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ +(uint32_t)(((const uint8_t *)(d))[0]) ) #endif #define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ do { \ char *_sfh_key=(char*)(key); \ uint32_t _sfh_tmp, _sfh_len = keylen; \ \ int _sfh_rem = _sfh_len & 3; \ _sfh_len >>= 2; \ hashv = 0xcafebabe; \ \ /* Main loop */ \ for (;_sfh_len > 0; _sfh_len--) { \ hashv += get16bits (_sfh_key); \ _sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \ hashv = (hashv << 16) ^ _sfh_tmp; \ _sfh_key += 2*sizeof (uint16_t); \ hashv += hashv >> 11; \ } \ \ /* Handle end cases */ \ switch (_sfh_rem) { \ case 3: hashv += get16bits (_sfh_key); \ hashv ^= hashv << 16; \ hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \ hashv += hashv >> 11; \ break; \ case 2: hashv += get16bits (_sfh_key); \ hashv ^= hashv << 11; \ hashv += hashv >> 17; \ break; \ case 1: hashv += *_sfh_key; \ hashv ^= hashv << 10; \ hashv += hashv >> 1; \ } \ \ /* Force "avalanching" of final 127 bits */ \ hashv ^= hashv << 3; \ hashv += hashv >> 5; \ hashv ^= hashv << 4; \ hashv += hashv >> 17; \ hashv ^= hashv << 25; \ hashv += hashv >> 6; \ bkt = hashv & (num_bkts-1); \ } while(0) #ifdef HASH_USING_NO_STRICT_ALIASING /* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. * MurmurHash uses the faster approach only on CPU's where we know it's safe. * * Note the preprocessor built-in defines can be emitted using: * * gcc -m64 -dM -E - < /dev/null (on gcc) * cc -## a.c (where a.c is a simple test file) (Sun Studio) */ #if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) #define MUR_GETBLOCK(p,i) p[i] #else /* non intel */ #define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0) #define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1) #define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2) #define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3) #define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) #if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) #define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) #define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) #define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) #else /* assume little endian non-intel */ #define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) #define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) #define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) #endif #define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ MUR_ONE_THREE(p)))) #endif #define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) #define MUR_FMIX(_h) \ do { \ _h ^= _h >> 16; \ _h *= 0x85ebca6b; \ _h ^= _h >> 13; \ _h *= 0xc2b2ae35l; \ _h ^= _h >> 16; \ } while(0) #define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ do { \ const uint8_t *_mur_data = (const uint8_t*)(key); \ const int _mur_nblocks = (keylen) / 4; \ uint32_t _mur_h1 = 0xf88D5353; \ uint32_t _mur_c1 = 0xcc9e2d51; \ uint32_t _mur_c2 = 0x1b873593; \ uint32_t _mur_k1 = 0; \ const uint8_t *_mur_tail; \ const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \ int _mur_i; \ for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \ _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ _mur_k1 *= _mur_c1; \ _mur_k1 = MUR_ROTL32(_mur_k1,15); \ _mur_k1 *= _mur_c2; \ \ _mur_h1 ^= _mur_k1; \ _mur_h1 = MUR_ROTL32(_mur_h1,13); \ _mur_h1 = _mur_h1*5+0xe6546b64; \ } \ _mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \ _mur_k1=0; \ switch((keylen) & 3) { \ case 3: _mur_k1 ^= _mur_tail[2] << 16; \ case 2: _mur_k1 ^= _mur_tail[1] << 8; \ case 1: _mur_k1 ^= _mur_tail[0]; \ _mur_k1 *= _mur_c1; \ _mur_k1 = MUR_ROTL32(_mur_k1,15); \ _mur_k1 *= _mur_c2; \ _mur_h1 ^= _mur_k1; \ } \ _mur_h1 ^= (keylen); \ MUR_FMIX(_mur_h1); \ hashv = _mur_h1; \ bkt = hashv & (num_bkts-1); \ } while(0) #endif /* HASH_USING_NO_STRICT_ALIASING */ /* key comparison function; return 0 if keys equal */ #define HASH_KEYCMP(a,b,len) memcmp(a,b,len) /* iterate over items in a known bucket to find desired item */ #define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ do { \ if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ else out=NULL; \ while (out) { \ if ((out)->hh.keylen == keylen_in) { \ if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \ } \ if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \ else out = NULL; \ } \ } while(0) /* add an item to a bucket */ #define HASH_ADD_TO_BKT(head,addhh) \ do { \ head.count++; \ (addhh)->hh_next = head.hh_head; \ (addhh)->hh_prev = NULL; \ if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ (head).hh_head=addhh; \ if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ && (addhh)->tbl->noexpand != 1) { \ HASH_EXPAND_BUCKETS((addhh)->tbl); \ } \ } while(0) /* remove an item from a given bucket */ #define HASH_DEL_IN_BKT(hh,head,hh_del) \ (head).count--; \ if ((head).hh_head == hh_del) { \ (head).hh_head = hh_del->hh_next; \ } \ if (hh_del->hh_prev) { \ hh_del->hh_prev->hh_next = hh_del->hh_next; \ } \ if (hh_del->hh_next) { \ hh_del->hh_next->hh_prev = hh_del->hh_prev; \ } /* Bucket expansion has the effect of doubling the number of buckets * and redistributing the items into the new buckets. Ideally the * items will distribute more or less evenly into the new buckets * (the extent to which this is true is a measure of the quality of * the hash function as it applies to the key domain). * * With the items distributed into more buckets, the chain length * (item count) in each bucket is reduced. Thus by expanding buckets * the hash keeps a bound on the chain length. This bounded chain * length is the essence of how a hash provides constant time lookup. * * The calculation of tbl->ideal_chain_maxlen below deserves some * explanation. First, keep in mind that we're calculating the ideal * maximum chain length based on the *new* (doubled) bucket count. * In fractions this is just n/b (n=number of items,b=new num buckets). * Since the ideal chain length is an integer, we want to calculate * ceil(n/b). We don't depend on floating point arithmetic in this * hash, so to calculate ceil(n/b) with integers we could write * * ceil(n/b) = (n/b) + ((n%b)?1:0) * * and in fact a previous version of this hash did just that. * But now we have improved things a bit by recognizing that b is * always a power of two. We keep its base 2 log handy (call it lb), * so now we can write this with a bit shift and logical AND: * * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) * */ #define HASH_EXPAND_BUCKETS(tbl) \ do { \ unsigned _he_bkt; \ unsigned _he_bkt_i; \ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ memset(_he_new_buckets, 0, \ 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ tbl->ideal_chain_maxlen = \ (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ tbl->nonideal_items = 0; \ for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ { \ _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ while (_he_thh) { \ _he_hh_nxt = _he_thh->hh_next; \ HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ tbl->nonideal_items++; \ _he_newbkt->expand_mult = _he_newbkt->count / \ tbl->ideal_chain_maxlen; \ } \ _he_thh->hh_prev = NULL; \ _he_thh->hh_next = _he_newbkt->hh_head; \ if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ _he_thh; \ _he_newbkt->hh_head = _he_thh; \ _he_thh = _he_hh_nxt; \ } \ } \ uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ tbl->num_buckets *= 2; \ tbl->log2_num_buckets++; \ tbl->buckets = _he_new_buckets; \ tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ (tbl->ineff_expands+1) : 0; \ if (tbl->ineff_expands > 1) { \ tbl->noexpand=1; \ uthash_noexpand_fyi(tbl); \ } \ uthash_expand_fyi(tbl); \ } while(0) /* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ /* Note that HASH_SORT assumes the hash handle name to be hh. * HASH_SRT was added to allow the hash handle name to be passed in. */ #define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) #define HASH_SRT(hh,head,cmpfcn) \ do { \ unsigned _hs_i; \ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ if (head) { \ _hs_insize = 1; \ _hs_looping = 1; \ _hs_list = &((head)->hh); \ while (_hs_looping) { \ _hs_p = _hs_list; \ _hs_list = NULL; \ _hs_tail = NULL; \ _hs_nmerges = 0; \ while (_hs_p) { \ _hs_nmerges++; \ _hs_q = _hs_p; \ _hs_psize = 0; \ for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ _hs_psize++; \ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ ((void*)((char*)(_hs_q->next) + \ (head)->hh.tbl->hho)) : NULL); \ if (! (_hs_q) ) break; \ } \ _hs_qsize = _hs_insize; \ while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ if (_hs_psize == 0) { \ _hs_e = _hs_q; \ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ ((void*)((char*)(_hs_q->next) + \ (head)->hh.tbl->hho)) : NULL); \ _hs_qsize--; \ } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ _hs_e = _hs_p; \ _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ ((void*)((char*)(_hs_p->next) + \ (head)->hh.tbl->hho)) : NULL); \ _hs_psize--; \ } else if (( \ cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ ) <= 0) { \ _hs_e = _hs_p; \ _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ ((void*)((char*)(_hs_p->next) + \ (head)->hh.tbl->hho)) : NULL); \ _hs_psize--; \ } else { \ _hs_e = _hs_q; \ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ ((void*)((char*)(_hs_q->next) + \ (head)->hh.tbl->hho)) : NULL); \ _hs_qsize--; \ } \ if ( _hs_tail ) { \ _hs_tail->next = ((_hs_e) ? \ ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ } else { \ _hs_list = _hs_e; \ } \ _hs_e->prev = ((_hs_tail) ? \ ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ _hs_tail = _hs_e; \ } \ _hs_p = _hs_q; \ } \ _hs_tail->next = NULL; \ if ( _hs_nmerges <= 1 ) { \ _hs_looping=0; \ (head)->hh.tbl->tail = _hs_tail; \ DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ } \ _hs_insize *= 2; \ } \ HASH_FSCK(hh,head); \ } \ } while (0) /* This function selects items from one hash into another hash. * The end result is that the selected items have dual presence * in both hashes. There is no copy of the items made; rather * they are added into the new hash through a secondary hash * hash handle that must be present in the structure. */ #define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ do { \ unsigned _src_bkt, _dst_bkt; \ void *_last_elt=NULL, *_elt; \ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ if (src) { \ for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ _src_hh; \ _src_hh = _src_hh->hh_next) { \ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ if (cond(_elt)) { \ _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ _dst_hh->key = _src_hh->key; \ _dst_hh->keylen = _src_hh->keylen; \ _dst_hh->hashv = _src_hh->hashv; \ _dst_hh->prev = _last_elt; \ _dst_hh->next = NULL; \ if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ if (!dst) { \ DECLTYPE_ASSIGN(dst,_elt); \ HASH_MAKE_TABLE(hh_dst,dst); \ } else { \ _dst_hh->tbl = (dst)->hh_dst.tbl; \ } \ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ (dst)->hh_dst.tbl->num_items++; \ _last_elt = _elt; \ _last_elt_hh = _dst_hh; \ } \ } \ } \ } \ HASH_FSCK(hh_dst,dst); \ } while (0) #define HASH_CLEAR(hh,head) \ do { \ if (head) { \ uthash_free((head)->hh.tbl->buckets, \ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ HASH_BLOOM_FREE((head)->hh.tbl); \ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ (head)=NULL; \ } \ } while(0) #ifdef NO_DECLTYPE #define HASH_ITER(hh,head,el,tmp) \ for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) #else #define HASH_ITER(hh,head,el,tmp) \ for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) #endif /* obtain a count of items in the hash */ #define HASH_COUNT(head) HASH_CNT(hh,head) #define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) typedef struct UT_hash_bucket { struct UT_hash_handle *hh_head; unsigned count; /* expand_mult is normally set to 0. In this situation, the max chain length * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If * the bucket's chain exceeds this length, bucket expansion is triggered). * However, setting expand_mult to a non-zero value delays bucket expansion * (that would be triggered by additions to this particular bucket) * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. * (The multiplier is simply expand_mult+1). The whole idea of this * multiplier is to reduce bucket expansions, since they are expensive, in * situations where we know that a particular bucket tends to be overused. * It is better to let its chain length grow to a longer yet-still-bounded * value, than to do an O(n) bucket expansion too often. */ unsigned expand_mult; } UT_hash_bucket; /* random signature used only to find hash tables in external analysis */ #define HASH_SIGNATURE 0xa0111fe1 #define HASH_BLOOM_SIGNATURE 0xb12220f2 typedef struct UT_hash_table { UT_hash_bucket *buckets; unsigned num_buckets, log2_num_buckets; unsigned num_items; struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ /* in an ideal situation (all buckets used equally), no bucket would have * more than ceil(#items/#buckets) items. that's the ideal chain length. */ unsigned ideal_chain_maxlen; /* nonideal_items is the number of items in the hash whose chain position * exceeds the ideal chain maxlen. these items pay the penalty for an uneven * hash distribution; reaching them in a chain traversal takes >ideal steps */ unsigned nonideal_items; /* ineffective expands occur when a bucket doubling was performed, but * afterward, more than half the items in the hash had nonideal chain * positions. If this happens on two consecutive expansions we inhibit any * further expansion, as it's not helping; this happens when the hash * function isn't a good fit for the key domain. When expansion is inhibited * the hash will still work, albeit no longer in constant time. */ unsigned ineff_expands, noexpand; uint32_t signature; /* used only to find hash tables in external analysis */ #ifdef HASH_BLOOM uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ uint8_t *bloom_bv; char bloom_nbits; #endif } UT_hash_table; typedef struct UT_hash_handle { struct UT_hash_table *tbl; void *prev; /* prev element in app order */ void *next; /* next element in app order */ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ struct UT_hash_handle *hh_next; /* next hh in bucket order */ void *key; /* ptr to enclosing struct's key */ unsigned keylen; /* enclosing struct's key len */ unsigned hashv; /* result of hash-fcn(key) */ } UT_hash_handle; #endif /* UTHASH_H */ sysdig-0.26.4/userspace/libscap/windows_hal.c000066400000000000000000000074761352731327100212200ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #define DRAGENT_WIN_HAL_C_ONLY #include #include "scap.h" #include "scap-int.h" #include "windows_hal.h" static int32_t addprocess_windows(wh_procinfo* wpi, scap_t* handle, char *error) { struct scap_threadinfo* tinfo; // // Allocate the procinfo object. // if((tinfo = scap_proc_alloc(handle)) == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "addprocess_windows memory allocation error"); return SCAP_FAILURE; } // // Fill the procinfo object // memset(tinfo, 0, sizeof(struct scap_threadinfo)); tinfo->pid = wpi->pid; tinfo->tid = wpi->tid; tinfo->ptid = wpi->ptid; snprintf(tinfo->comm, SCAP_MAX_PATH_SIZE, "%s", wpi->comm); snprintf(tinfo->exe, SCAP_MAX_PATH_SIZE, "%s", wpi->exe); snprintf(tinfo->exepath, SCAP_MAX_PATH_SIZE, "%s", wpi->exepath); snprintf(tinfo->args, SCAP_MAX_PATH_SIZE, "%s", wpi->args); tinfo->args_len = wpi->args_len; tinfo->vmsize_kb = wpi->vmsize_kb; tinfo->pfmajor = wpi->pfmajor; tinfo->pfminor = wpi->pfminor; tinfo->clone_ts = wpi->clone_ts; tinfo->tty = wpi->tty; wh_proc_perf_info pinfo = wh_wmi_get_proc_perf_info(handle->m_whh, tinfo->pid); if(pinfo.m_result != 0) { tinfo->vmrss_kb = pinfo.m_memory_bytes / 1024; tinfo->vmswap_kb = pinfo.m_swap_bytes / 1024; } else { tinfo->vmrss_kb = 0; tinfo->vmswap_kb = 0; } // // Done. Add the entry to the process table, or fire the notification callback // if(handle->m_proc_callback == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "process table construction in scap not supportted on windows"); return SCAP_FAILURE; // int32_t uth_status = SCAP_SUCCESS; // HASH_ADD_INT64(handle->m_proclist, pid, tinfo); // if(uth_status != SCAP_SUCCESS) // { // snprintf(error, SCAP_LASTERR_SIZE, "process table allocation error (2)"); // return SCAP_FAILURE; // } } else { handle->m_proc_callback(handle->m_proc_callback_context, handle, tinfo->tid, tinfo, NULL); free(tinfo); } return SCAP_SUCCESS; } typedef int (CALLBACK* LPFNDLLFUNC1)(); int32_t scap_proc_scan_proc_dir_windows(scap_t* handle, char *error) { wh_proclist wgpres; // // Get the system processes through WMI // wgpres = wh_wmi_get_procs(handle->m_whh); if(wgpres.m_result == 0) { snprintf(error, SCAP_LASTERR_SIZE, "%s", wh_getlasterror(handle->m_whh)); return SCAP_FAILURE; } // // While we're here, refresh the docker state and the process performance table // if(wh_is_docker_present(handle->m_whh)) { if(wh_docker_refresh(handle->m_whh) == 0) { snprintf(error, SCAP_LASTERR_SIZE, "%s", wh_getlasterror(handle->m_whh)); return SCAP_FAILURE; } } if(wh_wmi_update_procs_perf(handle->m_whh) == 0) { snprintf(error, SCAP_LASTERR_SIZE, "%s", wh_getlasterror(handle->m_whh)); return SCAP_FAILURE; } // // Add the received processes to the scap list // for(uint32_t j = 0; j < wgpres.m_count; j++) { wh_procinfo* wpi = &(wgpres.m_procs[j]); if(addprocess_windows(wpi, handle, error) != SCAP_SUCCESS) { return SCAP_FAILURE; } } return SCAP_SUCCESS; } sysdig-0.26.4/userspace/libscap/windows_hal.h000066400000000000000000000013121352731327100212040ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once int32_t scap_proc_scan_proc_dir_windows(scap_t* handle, char *error); sysdig-0.26.4/userspace/libsinsp/000077500000000000000000000000001352731327100167265ustar00rootroot00000000000000sysdig-0.26.4/userspace/libsinsp/.gitignore000066400000000000000000000000121352731327100207070ustar00rootroot00000000000000unit_testssysdig-0.26.4/userspace/libsinsp/CMakeLists.txt000066400000000000000000000112751352731327100214740ustar00rootroot00000000000000# # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # include_directories(./) include_directories(../../common) include_directories(../libscap) include_directories(../async) include_directories("${JSONCPP_INCLUDE}") include_directories("${LUAJIT_INCLUDE}") include_directories("${TBB_INCLUDE_DIR}") if(NOT WIN32 AND NOT APPLE) include_directories("${B64_INCLUDE}") include_directories("${CURSES_INCLUDE_DIR}") include_directories("${GRPC_INCLUDE}") include_directories("${JQ_INCLUDE}") include_directories("${OPENSSL_INCLUDE_DIR}") include_directories("${PROTOBUF_INCLUDE}") include_directories("${CMAKE_CURRENT_BINARY_DIR}") endif() if(NOT WIN32) include_directories("${CURL_INCLUDE_DIR}") endif() set(SINSP_SOURCES chisel.cpp chisel_api.cpp container.cpp container_engine/container_engine.cpp container_engine/docker_common.cpp container_info.cpp ctext.cpp cyclewriter.cpp cursescomponents.cpp cursestable.cpp cursesspectro.cpp cursesui.cpp event.cpp eventformatter.cpp dns_manager.cpp dumper.cpp fdinfo.cpp filter.cpp filterchecks.cpp gen_filter.cpp http_parser.c http_reason.cpp ifinfo.cpp json_query.cpp json_error_log.cpp k8s.cpp k8s_api_error.cpp k8s_api_handler.cpp k8s_component.cpp k8s_daemonset_handler.cpp k8s_deployment_handler.cpp k8s_dispatcher.cpp k8s_event_data.cpp k8s_event_handler.cpp k8s_handler.cpp k8s_namespace_handler.cpp k8s_net.cpp k8s_node_handler.cpp k8s_pod_handler.cpp k8s_pod_handler.cpp k8s_replicationcontroller_handler.cpp k8s_replicaset_handler.cpp k8s_service_handler.cpp k8s_state.cpp lua_parser.cpp lua_parser_api.cpp marathon_component.cpp marathon_http.cpp memmem.cpp tracers.cpp mesos_auth.cpp mesos.cpp mesos_collector.cpp mesos_component.cpp mesos_http.cpp mesos_state.cpp internal_metrics.cpp "${JSONCPP_LIB_SRC}" logger.cpp parsers.cpp prefix_search.cpp protodecoder.cpp threadinfo.cpp tuples.cpp sinsp.cpp stats.cpp table.cpp token_bucket.cpp sinsp_auth.cpp sinsp_curl.cpp stopwatch.cpp uri_parser.c uri.cpp user_event_logger.cpp utils.cpp user_event.cpp value_parser.cpp viewinfo.cpp ) if(WIN32) list(APPEND SINSP_SOURCES container_engine/docker_win.cpp) else() list(APPEND SINSP_SOURCES container_engine/docker_linux.cpp container_engine/libvirt_lxc.cpp container_engine/lxc.cpp container_engine/mesos.cpp container_engine/rkt.cpp container_engine/bpm.cpp runc.cpp) endif() if(NOT WIN32 AND NOT APPLE) list(APPEND SINSP_SOURCES grpc_channel_registry.cpp cri.cpp container_engine/cri.cpp ${CMAKE_CURRENT_BINARY_DIR}/cri.grpc.pb.cc ${CMAKE_CURRENT_BINARY_DIR}/cri.pb.cc) endif() add_library(sinsp STATIC ${SINSP_SOURCES}) target_link_libraries(sinsp scap "${CURL_LIBRARIES}" "${JSONCPP_LIB}" "${TBB_LIB}") if(USE_BUNDLED_LUAJIT) add_dependencies(sinsp luajit) endif() if(NOT WIN32) if(USE_BUNDLED_OPENSSL) add_dependencies(sinsp openssl) endif() if(USE_BUNDLED_CURL) add_dependencies(sinsp curl) endif() if(USE_BUNDLED_TBB) add_dependencies(sinsp tbb) endif() if(USE_BUNDLED_GRPC) add_dependencies(sinsp grpc) endif() if(NOT APPLE) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/cri.grpc.pb.cc ${CMAKE_CURRENT_BINARY_DIR}/cri.grpc.pb.h ${CMAKE_CURRENT_BINARY_DIR}/cri.pb.cc ${CMAKE_CURRENT_BINARY_DIR}/cri.pb.h COMMENT "Generate CRI grpc code" DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/cri.proto COMMAND ${PROTOC} -I ${CMAKE_CURRENT_SOURCE_DIR} --cpp_out=. ${CMAKE_CURRENT_SOURCE_DIR}/cri.proto COMMAND ${PROTOC} -I ${CMAKE_CURRENT_SOURCE_DIR} --grpc_out=. --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN} ${CMAKE_CURRENT_SOURCE_DIR}/cri.proto WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(sinsp "${GRPCPP_LIB}" "${GRPC_LIB}" "${PROTOBUF_LIB}" "${CARES_LIB}" "${JQ_LIB}" "${B64_LIB}" rt anl) endif() if(USE_BUNDLED_OPENSSL) target_link_libraries(sinsp "${OPENSSL_LIBRARY_SSL}" "${OPENSSL_LIBRARY_CRYPTO}") else() target_link_libraries(sinsp "${OPENSSL_LIBRARIES}") endif() target_link_libraries(sinsp "${LUAJIT_LIB}" dl pthread) else() target_link_libraries(sinsp "${LUAJIT_LIB}") endif() sysdig-0.26.4/userspace/libsinsp/capture_stats_source.h000066400000000000000000000026171352731327100233460ustar00rootroot00000000000000/* Copyright (C) 2019 Sysdig Inc. This file is part of sysdig. 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. */ #pragma once #include "sinsp_public.h" class scap_stats; /** * Interface to an object that can provide capture statistics. * * Note that the intention here is to apply the Interface Segregation * Principle (ISP) to class sinsp. Some clients of sinsp need only the * get_capture_stats() API, and this interface exposes only that API. Do * not add additional APIs here. If some client of sinsp needs a different * set of APIs, introduce a new interface. */ class SINSP_PUBLIC capture_stats_source { public: virtual ~capture_stats_source() = default; /** * Fill the given structure with statistics about the currently * open capture. * * @note This may not work for a file-based capture source. * * @param[out] stats The capture statistics */ virtual void get_capture_stats(scap_stats* stats) = 0; }; sysdig-0.26.4/userspace/libsinsp/cgroup_list_counter.h000066400000000000000000000055421352731327100231760ustar00rootroot00000000000000#pragma once #include #include "logger.h" namespace libsinsp { /** * Simple helper to read a comma-separated list that includes ranges and * determine the total count of the values within. * Examples: 1,4-5 = 3; 0-15 = 16; 3,7,11 = 3 * * See the "List Format" section of * http://man7.org/linux/man-pages/man7/cpuset.7.html * * Returns -1 if string is invalid. */ class cgroup_list_counter { public: const int INVALID_CPU_COUNT = -1; /** * Return the number of elements given by the buffer. If needed, log at the * given log-level. */ int operator ()(const char *buffer, sinsp_logger::severity log_level) { reset(); int cpu_count = 0; try { const char *position = buffer; for(; '\0' != *position; ++position) { if ('-' == *position) { if (nullptr == m_section_start) { throw std::runtime_error("duplicate range indicator before start"); } if (nullptr != m_range_indicator) { throw std::runtime_error("duplicate range indicators"); } m_range_indicator = position; } else if (',' == *position) { cpu_count += process_section(m_section_start, position, m_range_indicator); reset(); } else if (nullptr == m_section_start) { m_section_start = position; } } // There is never a trailing comma so always process the // final section cpu_count += process_section(m_section_start, position, m_range_indicator); } catch (const std::exception& ex) { g_logger.format(log_level, "Invalid List Format: %s. Detail: %s", buffer, ex.what()); return INVALID_CPU_COUNT; } return cpu_count; } private: static int process_number(const char *section_start, const char *section_end) { std::string section(section_start, section_end - section_start); return std::stoi(section.c_str()); } static int process_section(const char *section_start, const char *section_end, const char *range_indicator) { if (nullptr == section_start) { throw std::runtime_error("invalid end of section before start of section"); } if (nullptr == section_end) { throw std::runtime_error("invalid end of section"); } if (section_end <= section_start) { throw std::runtime_error("invalid section"); } if (range_indicator) { // Split into two sections int first = process_number(section_start, range_indicator); int second = process_number(range_indicator + 1, section_end); if (second <= first) { throw std::runtime_error("invalid range"); } return second - first + 1; } // We don't care what the value is, we just want to know that it is a number (void)process_number(section_start, section_end); return 1; } void reset() { m_section_start = nullptr; m_range_indicator = nullptr; } const char *m_section_start = nullptr; const char *m_range_indicator = nullptr; }; }sysdig-0.26.4/userspace/libsinsp/chisel.cpp000066400000000000000000001064331352731327100207100ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #ifdef _WIN32 #include #else #include #include #include #endif #include #include #include "sinsp.h" #include "sinsp_int.h" #include "chisel.h" #include "chisel_api.h" #include "filter.h" #include "filterchecks.h" #include "table.h" #ifdef HAS_CHISELS #define HAS_LUA_CHISELS #ifdef HAS_LUA_CHISELS extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" } #endif extern vector* g_chisel_dirs; extern sinsp_filter_check_list g_filterlist; extern sinsp_evttables g_infotables; /////////////////////////////////////////////////////////////////////////////// // 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_sysdig [] = { {"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_sysdig", &lua_cbacks::run_sysdig}, {"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}, #ifdef HAS_ANALYZER {"push_metric", &lua_cbacks::push_metric}, #endif {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(); } } 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); } else { m_formatter = new sinsp_evt_formatter(m_inspector, formatterstr); } } #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) { 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); } 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 char* 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 sinsp_field_aggregation sinsp_chisel::string_to_aggregation(string ag) { sinsp_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; sinsp_field_aggregation aggregation = A_NONE; sinsp_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(sinsp_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(sinsp_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; sinsp_view_info::viewtype vt = sinsp_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 = sinsp_view_info::T_TABLE; } else if(tmpstr == "list") { vt = sinsp_view_info::T_LIST; } else if(tmpstr == "spectrogram") { vt = sinsp_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 = sinsp_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 = lua_open(); if(ls == NULL) { return false; } luaL_openlibs(ls); // // Load our own lua libs // luaL_openlib(ls, "sysdig", ll_sysdig, 0); luaL_openlib(ls, "chisel", ll_chisel, 0); luaL_openlib(ls, "evt", ll_evt, 0); // // Add our chisel paths to package.path // for(vector::const_iterator it = g_chisel_dirs->begin(); it != g_chisel_dirs->end(); ++it) { string path(it->m_dir); path += "?.lua"; add_lua_package_path(ls, path.c_str()); } // // 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; }; static filename split_filename(string const &fname) { filename res; string::size_type idx = fname.rfind('.'); if(idx == std::string::npos) { res.valid = false; } else { res.valid = true; res.name = fname.substr(0, idx); res.ext = fname.substr(idx+1); } return res; } // // 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) { for(vector::const_iterator it = g_chisel_dirs->begin(); it != g_chisel_dirs->end(); ++it) { if(string(it->m_dir).empty()) { continue; } tinydir_dir dir = {}; tinydir_open(&dir, it->m_dir.c_str()); while(dir.has_next) { tinydir_file file; tinydir_readfile(&dir, &file); string fpath(file.path); bool add_to_vector = false; chisel_desc cd; filename fn = split_filename(string(file.name)); if(fn.ext != "sc" && fn.ext != "lua") { goto next_file; } for(vector::const_iterator it_desc = chisel_descs->begin(); it_desc != chisel_descs->end(); ++it_desc) { if(fn.name == it_desc->m_name) { goto next_file; } } cd.m_name = fn.name; #ifdef HAS_LUA_CHISELS if(fn.ext == "lua") { add_to_vector = init_lua_chisel(cd, fpath); } if(add_to_vector) { chisel_descs->push_back(cd); } #endif next_file: tinydir_next(&dir); } tinydir_close(&dir); } } // // 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) { m_filename = cmdstr; trim(cmdstr); ifstream is; // // 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); } } #ifdef HAS_LUA_CHISELS // // Load the file // std::istreambuf_iterator eos; std::string scriptstr(std::istreambuf_iterator(is), eos); // // Open the script // m_ls = lua_open(); luaL_openlibs(m_ls); // // Load our own lua libs // luaL_openlib(m_ls, "sysdig", ll_sysdig, 0); luaL_openlib(m_ls, "chisel", ll_chisel, 0); luaL_openlib(m_ls, "evt", ll_evt, 0); // // Add our chisel paths to package.path // for(uint32_t j = 0; j < g_chisel_dirs->size(); j++) { string path(g_chisel_dirs->at(j).m_dir); path += "?.lua"; add_lua_package_path(m_ls, path.c_str()); } // // 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"); } try { parse_lua_chisel_args(m_ls, &m_lua_script_info); } catch(sinsp_exception& e) { throw e; } // // 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 sinsp_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->m_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; } #endif // HAS_CHISELS sysdig-0.26.4/userspace/libsinsp/chisel.h000066400000000000000000000100251352731327100203440ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #ifdef HAS_CHISELS class sinsp_filter_check; class sinsp_evt_formatter; class sinsp_view_info; typedef struct lua_State lua_State; /** @defgroup filter Filtering events * Filtering infrastructure. * @{ */ /*! \brief This is the class that compiles and runs sysdig-type filters. */ typedef struct chiseldir_info { bool m_need_to_resolve; std::string m_dir; }chiseldir_info; class chiselarg_desc { public: chiselarg_desc(string name, string type, string description, bool optional) { m_name = name; m_type = type; m_description = description; m_optional = optional; } string m_name; string m_type; string m_description; bool m_optional; }; class chisel_desc { public: void reset() { m_name = ""; m_description = ""; m_category = ""; m_shortdesc = ""; m_args.clear(); } string m_name; string m_description; string m_category; string m_shortdesc; vector m_args; sinsp_view_info m_viewinfo; }; class chiselinfo { public: chiselinfo(sinsp* inspector); void init(string filterstr, string formatterstr); void set_filter(string filterstr); void set_formatter(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; string m_nextrun_args; bool m_end_capture; private: sinsp* m_inspector; }; class SINSP_PUBLIC sinsp_chisel { public: sinsp_chisel(sinsp* inspector, string filename); ~sinsp_chisel(); static void add_lua_package_path(lua_State* ls, const char* path); static void get_chisel_list(vector* chisel_descs); void load(string cmdstr); 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(string args); void set_args(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 string* args); chisel_desc* get_lua_script_info() { return &m_lua_script_info; } private: bool openfile(string filename, OUT ifstream* is); void free_lua_chisel(); static sinsp_field_aggregation string_to_aggregation(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, string const &path); void first_event_inits(sinsp_evt* evt); sinsp* m_inspector; string m_description; vector m_argvals; 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; vector m_allocated_fltchecks; char m_lua_fld_storage[PPM_MAX_ARG_SIZE]; chiselinfo* m_lua_cinfo; string m_new_chisel_to_exec; int m_udp_socket; struct sockaddr_in m_serveraddr; friend class lua_cbacks; }; /*@}*/ #endif // HAS_CHISELS sysdig-0.26.4/userspace/libsinsp/chisel_api.cpp000066400000000000000000001030221352731327100215300ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #ifdef _WIN32 #include #else #include #include #include #include #endif #include #include #include "sinsp.h" #include "sinsp_int.h" #include "chisel.h" #include "chisel_api.h" #include "filter.h" #include "filterchecks.h" #ifdef HAS_ANALYZER #include "analyzer.h" #endif #ifdef HAS_CHISELS #define HAS_LUA_CHISELS #ifdef HAS_LUA_CHISELS extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" } #endif extern vector* g_chisel_dirs; extern sinsp_filter_check_list g_filterlist; extern sinsp_evttables g_infotables; void lua_stackdump(lua_State *L); /////////////////////////////////////////////////////////////////////////////// // 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: lua_pushnumber(ls, *(uint8_t*)rawval); return 1; case PT_PORT: // This can be resolved in the future case PT_FLAGS16: case PT_UINT16: lua_pushnumber(ls, *(uint16_t*)rawval); return 1; case PT_FLAGS32: case PT_UINT32: case PT_MODE: case PT_UID: case PT_GID: 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: 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)) { strcpy(address, ""); } strncpy(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) { sinsp_evt_param *parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(uint16_t)); uint16_t evid = *(uint16_t *)parinfo->m_val; evname = g_infotables.m_syscall_info_table[evid].name; } 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"); } sinsp_filter_check* chk = g_filterlist.new_filter_check_from_fldname(fld, inspector, false); 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 // sysdig.request_field() before. // lua_pushnil(ls); return 1; } uint32_t vlen; uint8_t* rawval = chk->extract(evt, &vlen); if(rawval != NULL) { return rawval_to_lua_stack(ls, rawval, chk->get_field_info()->m_type, vlen); } 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(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(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_sysdig(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) { unordered_map::iterator fdit; uint32_t j; sinsp_filter_compiler* compiler = NULL; sinsp_filter* filter = NULL; 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 = new sinsp_filter_compiler(ch->m_inspector, filterstr, true); filter = compiler->compile(); } catch(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->m_lastevent_ts; tscapevt.type = PPME_SYSCALL_READ_X; tscapevt.len = 0; tscapevt.nparams = 0; tevt.m_inspector = ch->m_inspector; tevt.m_info = &(g_infotables.m_event_info[PPME_SYSCALL_READ_X]); tevt.m_pevt = NULL; tevt.m_cpuid = 0; tevt.m_evtnum = 0; tevt.m_pevt = &tscapevt; } } 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(filter != NULL) { bool match = false; for(fdit = fdtable->m_table.begin(); fdit != fdtable->m_table.end(); ++fdit) { tevt.m_tinfo = &tinfo; tevt.m_fdinfo = &(fdit->second); tscapevt.tid = tinfo.m_tid; int64_t tlefd = tevt.m_tinfo->m_lastevent_fd; tevt.m_tinfo->m_lastevent_fd = fdit->first; if(filter->run(&tevt)) { match = true; break; } tevt.m_tinfo->m_lastevent_fd = tlefd; } 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_uid); lua_settable(ls, -3); lua_pushliteral(ls, "gid"); lua_pushnumber(ls, (uint32_t)tinfo.m_gid); lua_settable(ls, -3); lua_pushliteral(ls, "nchilds"); lua_pushnumber(ls, (uint32_t)tinfo.m_nchilds); 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 // string username; unordered_map::const_iterator uit; const unordered_map* userlist = ch->m_inspector->get_userlist(); ASSERT(userlist->size() != 0); if(tinfo.m_uid == 0xffffffff) { username = ""; } else { uit = userlist->find(tinfo.m_uid); if(uit == userlist->end()) { username = ""; } else { ASSERT(uit->second != NULL); username = uit->second->name; } } lua_pushliteral(ls, "username"); lua_pushstring(ls, username.c_str()); 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) { for(fdit = fdtable->m_table.begin(); fdit != fdtable->m_table.end(); ++fdit) { tevt.m_tinfo = &tinfo; tevt.m_fdinfo = &(fdit->second); tscapevt.tid = tinfo.m_tid; int64_t tlefd = tevt.m_tinfo->m_lastevent_fd; tevt.m_tinfo->m_lastevent_fd = fdit->first; if(filter != NULL) { if(filter->run(&tevt) == false) { continue; } } tevt.m_tinfo->m_lastevent_fd = tlefd; lua_newtable(ls); if(!barebone) { lua_pushliteral(ls, "name"); lua_pushstring(ls, fdit->second.tostring_clean().c_str()); lua_settable(ls, -3); lua_pushliteral(ls, "type"); lua_pushstring(ls, fdit->second.get_typestring()); lua_settable(ls, -3); } scap_fd_type evt_type = fdit->second.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*)&(fdit->second.m_sockinfo.m_ipv4info.m_fields.m_sip); sip = (uint8_t*)&(fdit->second.m_sockinfo.m_ipv4info.m_fields.m_dip); cport = fdit->second.m_sockinfo.m_ipv4info.m_fields.m_sport; sport = fdit->second.m_sockinfo.m_ipv4info.m_fields.m_dport; is_server = fdit->second.is_role_server(); } else if (evt_type == SCAP_FD_IPV4_SERVSOCK) { include_client = false; af = AF_INET; cip = NULL; sip = (uint8_t*)&(fdit->second.m_sockinfo.m_ipv4serverinfo.m_ip); sport = fdit->second.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*)&(fdit->second.m_sockinfo.m_ipv6info.m_fields.m_sip); sip = (uint8_t*)&(fdit->second.m_sockinfo.m_ipv6info.m_fields.m_dip); cport = fdit->second.m_sockinfo.m_ipv6info.m_fields.m_sport; sport = fdit->second.m_sockinfo.m_ipv6info.m_fields.m_dport; is_server = fdit->second.is_role_server(); } else { include_client = false; af = AF_INET6; cip = NULL; sip = (uint8_t*)&(fdit->second.m_sockinfo.m_ipv6serverinfo.m_ip); sport = fdit->second.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))) { strcpy(sipbuf, ""); } if(cip) { if(NULL == inet_ntop(af, cip, cipbuf, sizeof(cipbuf))) { strcpy(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 = fdit->second.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)fdit->first); } } lua_settable(ls,-3); // // Set the key for this entry // lua_rawseti(ls,-2, (uint32_t)tinfo.m_tid); return true; }); if(filter) { delete filter; } 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) { unordered_map::iterator fdit; 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 unordered_map* ctable = ch->m_inspector->m_container_manager.get_containers(); ASSERT(ctable != NULL); 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); } 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->m_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; } g_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, (struct 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; } #ifdef HAS_ANALYZER int lua_cbacks::push_metric(lua_State *ls) { statsd_metric metric; metric.m_type = statsd_metric::type_t::GAUGE; 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"); } inspector->m_analyzer->add_chisel_metric(&metric); return 0; } #endif // HAS_ANALYZER #endif // HAS_LUA_CHISELS #endif // HAS_CHISELS sysdig-0.26.4/userspace/libsinsp/chisel_api.h000066400000000000000000000051571352731327100212070ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #ifdef HAS_CHISELS 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_sysdig(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); #ifdef HAS_ANALYZER static int push_metric(lua_State *ls); #endif private: static int get_thread_table_int(lua_State *ls, bool include_fds, bool barebone); }; #endif // HAS_CHISELS sysdig-0.26.4/userspace/libsinsp/container.cpp000066400000000000000000000337261352731327100214270ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "container_engine/cri.h" #include "container_engine/docker.h" #include "container_engine/rkt.h" #include "container_engine/libvirt_lxc.h" #include "container_engine/lxc.h" #include "container_engine/mesos.h" #include "container_engine/bpm.h" #include "sinsp.h" #include "sinsp_int.h" #include "container.h" #include "utils.h" using namespace libsinsp; sinsp_container_manager::sinsp_container_manager(sinsp* inspector) : m_inspector(inspector), m_last_flush_time_ns(0) { } sinsp_container_manager::~sinsp_container_manager() { } bool sinsp_container_manager::remove_inactive_containers() { bool res = false; if(m_last_flush_time_ns == 0) { m_last_flush_time_ns = m_inspector->m_lastevent_ts - m_inspector->m_inactive_container_scan_time_ns + 30 * ONE_SECOND_IN_NS; } if(m_inspector->m_lastevent_ts > m_last_flush_time_ns + m_inspector->m_inactive_container_scan_time_ns) { res = true; m_last_flush_time_ns = m_inspector->m_lastevent_ts; g_logger.format(sinsp_logger::SEV_INFO, "Flushing container table"); set containers_in_use; threadinfo_map_t* threadtable = m_inspector->m_thread_manager->get_threads(); threadtable->loop([&] (const sinsp_threadinfo& tinfo) { if(!tinfo.m_container_id.empty()) { containers_in_use.insert(tinfo.m_container_id); } return true; }); for(unordered_map::iterator it = m_containers.begin(); it != m_containers.end();) { if(containers_in_use.find(it->first) == containers_in_use.end()) { for(const auto &remove_cb : m_remove_callbacks) { remove_cb(m_containers[it->first]); } m_containers.erase(it++); } else { ++it; } } } return res; } sinsp_container_info* sinsp_container_manager::get_container(const string& container_id) { auto it = m_containers.find(container_id); if(it != m_containers.end()) { return &it->second; } return nullptr; } bool sinsp_container_manager::resolve_container(sinsp_threadinfo* tinfo, bool query_os_for_missing_info) { ASSERT(tinfo); bool matches = false; tinfo->m_container_id = ""; if (m_inspector->m_parser->m_fd_listener) { matches = m_inspector->m_parser->m_fd_listener->on_resolve_container(this, tinfo, query_os_for_missing_info); } // Delayed so there's a chance to set alternate socket paths, // timeouts, after creation but before inspector open. if(m_container_engines.size() == 0) { create_engines(); } for(auto &eng : m_container_engines) { matches = matches || eng->resolve(this, tinfo, query_os_for_missing_info); if(matches) { break; } } // Also possibly set the category for the threadinfo identify_category(tinfo); return matches; } string sinsp_container_manager::container_to_json(const sinsp_container_info& container_info) { Json::Value obj; Json::Value& container = obj["container"]; container["id"] = container_info.m_id; container["type"] = container_info.m_type; container["name"] = container_info.m_name; container["image"] = container_info.m_image; container["imageid"] = container_info.m_imageid; container["imagerepo"] = container_info.m_imagerepo; container["imagetag"] = container_info.m_imagetag; container["imagedigest"] = container_info.m_imagedigest; container["privileged"] = container_info.m_privileged; container["is_pod_sandbox"] = container_info.m_is_pod_sandbox; Json::Value mounts = Json::arrayValue; for (auto &mntinfo : container_info.m_mounts) { Json::Value mount; mount["Source"] = mntinfo.m_source; mount["Destination"] = mntinfo.m_dest; mount["Mode"] = mntinfo.m_mode; mount["RW"] = mntinfo.m_rdwr; mount["Propagation"] = mntinfo.m_propagation; mounts.append(mount); } container["Mounts"] = mounts; sinsp_container_info::container_health_probe::add_health_probes(container_info.m_health_probes, container); char addrbuff[100]; uint32_t iph = htonl(container_info.m_container_ip); inet_ntop(AF_INET, &iph, addrbuff, sizeof(addrbuff)); container["ip"] = addrbuff; Json::Value port_mappings = Json::arrayValue; for(auto &mapping : container_info.m_port_mappings) { Json::Value jmap; jmap["HostIp"] = mapping.m_host_ip; jmap["HostPort"] = mapping.m_host_port; jmap["ContainerPort"] = mapping.m_container_port; port_mappings.append(jmap); } container["port_mappings"] = port_mappings; Json::Value labels; for (auto &pair : container_info.m_labels) { labels[pair.first] = pair.second; } container["labels"] = labels; Json::Value env_vars = Json::arrayValue; for (auto &var : container_info.m_env) { // Only append a limited set of mesos/marathon-related // environment variables. if(var.find("MESOS") != std::string::npos || var.find("MARATHON") != std::string::npos || var.find("mesos") != std::string::npos) { env_vars.append(var); } } container["env"] = env_vars; container["memory_limit"] = (Json::Value::Int64) container_info.m_memory_limit; container["swap_limit"] = (Json::Value::Int64) container_info.m_swap_limit; container["cpu_shares"] = (Json::Value::Int64) container_info.m_cpu_shares; container["cpu_quota"] = (Json::Value::Int64) container_info.m_cpu_quota; container["cpu_period"] = (Json::Value::Int64) container_info.m_cpu_period; container["cpuset_cpu_count"] = (Json::Value::Int) container_info.m_cpuset_cpu_count; if(!container_info.m_mesos_task_id.empty()) { container["mesos_task_id"] = container_info.m_mesos_task_id; } container["metadata_deadline"] = (Json::Value::UInt64) container_info.m_metadata_deadline; return Json::FastWriter().write(obj); } bool sinsp_container_manager::container_to_sinsp_event(const string& json, sinsp_evt* evt, shared_ptr tinfo) { size_t totlen = sizeof(scap_evt) + sizeof(uint16_t) + json.length() + 1; ASSERT(evt->m_pevt_storage == nullptr); evt->m_pevt_storage = new char[totlen]; evt->m_pevt = (scap_evt *) evt->m_pevt_storage; evt->m_cpuid = 0; evt->m_evtnum = 0; evt->m_inspector = m_inspector; scap_evt* scapevt = evt->m_pevt; if(m_inspector->m_lastevent_ts == 0) { // This can happen at startup when containers are // being created as a part of the initial process // scan. scapevt->ts = sinsp_utils::get_current_time_ns(); } else { scapevt->ts = m_inspector->m_lastevent_ts; } scapevt->tid = -1; scapevt->len = (uint32_t)totlen; scapevt->type = PPME_CONTAINER_JSON_E; scapevt->nparams = 1; uint16_t* lens = (uint16_t*)((char *)scapevt + sizeof(struct ppm_evt_hdr)); char* valptr = (char*)lens + sizeof(uint16_t); *lens = (uint16_t)json.length() + 1; memcpy(valptr, json.c_str(), *lens); evt->init(); evt->m_tinfo_ref = tinfo; evt->m_tinfo = tinfo.get(); return true; } const unordered_map* sinsp_container_manager::get_containers() { return &m_containers; } void sinsp_container_manager::add_container(const sinsp_container_info& container_info, sinsp_threadinfo *thread_info) { m_containers[container_info.m_id] = container_info; for(const auto &new_cb : m_new_callbacks) { new_cb(m_containers[container_info.m_id], thread_info); } } void sinsp_container_manager::notify_new_container(const sinsp_container_info& container_info) { sinsp_evt *evt = new sinsp_evt(); if(container_to_sinsp_event(container_to_json(container_info), evt, container_info.get_tinfo(m_inspector))) { g_logger.format(sinsp_logger::SEV_DEBUG, "notify_new_container (%s): created CONTAINER_JSON event, queuing to inspector", container_info.m_id.c_str()); std::shared_ptr cevt(evt); // Enqueue it onto the queue of pending container events for the inspector m_inspector->m_pending_container_evts.push(cevt); } else { g_logger.format(sinsp_logger::SEV_ERROR, "notify_new_container (%s): could not create CONTAINER_JSON event, dropping", container_info.m_id.c_str()); delete evt; } } void sinsp_container_manager::dump_containers(scap_dumper_t* dumper) { for(unordered_map::const_iterator it = m_containers.begin(); it != m_containers.end(); ++it) { sinsp_evt evt; if(container_to_sinsp_event(container_to_json(it->second), &evt, it->second.get_tinfo(m_inspector))) { int32_t res = scap_dump(m_inspector->m_h, dumper, evt.m_pevt, evt.m_cpuid, 0); if(res != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); } } } } string sinsp_container_manager::get_container_name(sinsp_threadinfo* tinfo) { string res; if(tinfo->m_container_id.empty()) { res = "host"; } else { const sinsp_container_info *container_info = get_container(tinfo->m_container_id); if(!container_info) { return NULL; } if(container_info->m_name.empty()) { return NULL; } res = container_info->m_name; } return res; } void sinsp_container_manager::identify_category(sinsp_threadinfo *tinfo) { if(tinfo->m_container_id.empty()) { return; } sinsp_container_info *cinfo = get_container(tinfo->m_container_id); if(!cinfo) { return; } if(tinfo->m_vpid == 1) { if(g_logger.get_severity() >= sinsp_logger::SEV_DEBUG) { g_logger.format(sinsp_logger::SEV_DEBUG, "identify_category (%ld) (%s): initial process for container, assigning CAT_CONTAINER", tinfo->m_tid, tinfo->m_comm.c_str()); } tinfo->m_category = sinsp_threadinfo::CAT_CONTAINER; return; } // Categories are passed from parent to child threads sinsp_threadinfo* ptinfo = tinfo->get_parent_thread(); if(ptinfo && ptinfo->m_category != sinsp_threadinfo::CAT_NONE) { if(g_logger.get_severity() >= sinsp_logger::SEV_DEBUG) { g_logger.format(sinsp_logger::SEV_DEBUG, "identify_category (%ld) (%s): taking parent category %d", tinfo->m_tid, tinfo->m_comm.c_str(), ptinfo->m_category); } tinfo->m_category = ptinfo->m_category; return; } if(!cinfo->m_metadata_complete) { if(g_logger.get_severity() >= sinsp_logger::SEV_DEBUG) { g_logger.format(sinsp_logger::SEV_DEBUG, "identify_category (%ld) (%s): container metadata incomplete", tinfo->m_tid, tinfo->m_comm.c_str()); } return; } // Otherwise, the thread is a part of a container health probe if: // // 1. the comm and args match one of the container's health probes // 2. we traverse the parent state and do *not* find vpid=1, // or find a process not in a container // // This indicates the initial process of the health probe. sinsp_container_info::container_health_probe::probe_type ptype = cinfo->match_health_probe(tinfo); if(ptype == sinsp_container_info::container_health_probe::PT_NONE) { g_logger.format(sinsp_logger::SEV_DEBUG, "identify_category (%ld) (%s): container health probe PT_NONE", tinfo->m_tid, tinfo->m_comm.c_str()); return; } bool found_container_init = false; sinsp_threadinfo::visitor_func_t visitor = [&found_container_init] (sinsp_threadinfo *ptinfo) { if(ptinfo->m_vpid == 1 && !ptinfo->m_container_id.empty()) { found_container_init = true; return false; } return true; }; tinfo->traverse_parent_state(visitor); if(!found_container_init) { g_logger.format(sinsp_logger::SEV_DEBUG, "identify_category (%ld) (%s): not under container init, assigning category %s", tinfo->m_tid, tinfo->m_comm.c_str(), sinsp_container_info::container_health_probe::probe_type_names[ptype].c_str()); // Each health probe type maps to a command category switch(ptype) { case sinsp_container_info::container_health_probe::PT_NONE: case sinsp_container_info::container_health_probe::PT_END: break; case sinsp_container_info::container_health_probe::PT_HEALTHCHECK: tinfo->m_category = sinsp_threadinfo::CAT_HEALTHCHECK; break; case sinsp_container_info::container_health_probe::PT_LIVENESS_PROBE: tinfo->m_category = sinsp_threadinfo::CAT_LIVENESS_PROBE; break; case sinsp_container_info::container_health_probe::PT_READINESS_PROBE: tinfo->m_category = sinsp_threadinfo::CAT_READINESS_PROBE; break; } } } void sinsp_container_manager::subscribe_on_new_container(new_container_cb callback) { m_new_callbacks.emplace_back(callback); } void sinsp_container_manager::subscribe_on_remove_container(remove_container_cb callback) { m_remove_callbacks.emplace_back(callback); } void sinsp_container_manager::create_engines() { m_container_engines.emplace_back(new container_engine::docker()); #ifndef CYGWING_AGENT #if defined(HAS_CAPTURE) m_container_engines.emplace_back(new container_engine::cri()); #endif m_container_engines.emplace_back(new container_engine::lxc()); m_container_engines.emplace_back(new container_engine::libvirt_lxc()); m_container_engines.emplace_back(new container_engine::mesos()); m_container_engines.emplace_back(new container_engine::rkt()); m_container_engines.emplace_back(new container_engine::bpm()); #endif } void sinsp_container_manager::cleanup() { for(auto &eng : m_container_engines) { eng->cleanup(); } } void sinsp_container_manager::set_query_docker_image_info(bool query_image_info) { libsinsp::container_engine::docker_async_source::set_query_image_info(query_image_info); } void sinsp_container_manager::set_cri_extra_queries(bool extra_queries) { #if defined(HAS_CAPTURE) libsinsp::container_engine::cri::set_extra_queries(extra_queries); #endif } void sinsp_container_manager::set_cri_socket_path(const std::string &path) { #if defined(HAS_CAPTURE) libsinsp::container_engine::cri::set_cri_socket_path(path); #endif } void sinsp_container_manager::set_cri_timeout(int64_t timeout_ms) { #if defined(HAS_CAPTURE) libsinsp::container_engine::cri::set_cri_timeout(timeout_ms); #endif } sysdig-0.26.4/userspace/libsinsp/container.h000066400000000000000000000064171352731327100210710ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #include "container_info.h" #if !defined(_WIN32) && !defined(CYGWING_AGENT) && defined(HAS_CAPTURE) #include #include #include #endif #include "container_engine/container_engine.h" class sinsp_container_manager { public: sinsp_container_manager(sinsp* inspector); virtual ~sinsp_container_manager(); const unordered_map* get_containers(); bool remove_inactive_containers(); void add_container(const sinsp_container_info& container_info, sinsp_threadinfo *thread); sinsp_container_info * get_container(const string &id); void notify_new_container(const sinsp_container_info& container_info); template bool resolve_container_impl(sinsp_threadinfo* tinfo, bool query_os_for_missing_info); template bool resolve_container_impl(sinsp_threadinfo* tinfo, bool query_os_for_missing_info); bool resolve_container(sinsp_threadinfo* tinfo, bool query_os_for_missing_info); void dump_containers(scap_dumper_t* dumper); string get_container_name(sinsp_threadinfo* tinfo); // Set tinfo's m_category based on the container context. It // will *not* change any category to NONE, so a threadinfo // that initially has a category will retain its category // across execs e.g. "sh -c /bin/true" execing /bin/true. void identify_category(sinsp_threadinfo *tinfo); bool container_exists(const string& container_id) const { return m_containers.find(container_id) != m_containers.end(); } typedef std::function new_container_cb; typedef std::function remove_container_cb; void subscribe_on_new_container(new_container_cb callback); void subscribe_on_remove_container(remove_container_cb callback); void create_engines(); void cleanup(); void set_query_docker_image_info(bool query_image_info); void set_cri_extra_queries(bool extra_queries); void set_cri_socket_path(const std::string& path); void set_cri_timeout(int64_t timeout_ms); sinsp* get_inspector() { return m_inspector; } private: string container_to_json(const sinsp_container_info& container_info); bool container_to_sinsp_event(const string& json, sinsp_evt* evt, shared_ptr tinfo); string get_docker_env(const Json::Value &env_vars, const string &mti); std::list> m_container_engines; sinsp* m_inspector; unordered_map m_containers; uint64_t m_last_flush_time_ns; list m_new_callbacks; list m_remove_callbacks; friend class test_helper; }; sysdig-0.26.4/userspace/libsinsp/container_engine/000077500000000000000000000000001352731327100222355ustar00rootroot00000000000000sysdig-0.26.4/userspace/libsinsp/container_engine/bpm.cpp000066400000000000000000000037131352731327100235230ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "container_engine/bpm.h" #include "sinsp.h" using namespace libsinsp::container_engine; bool bpm::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) { sinsp_container_info container_info; bool matches = false; for(auto it = tinfo->m_cgroups.begin(); it != tinfo->m_cgroups.end(); ++it) { string cgroup = it->second; size_t pos; // // Non-systemd and systemd BPM // pos = cgroup.find("bpm-"); if(pos != string::npos) { auto id_start = pos + sizeof("bpm-") - 1; auto id_end = cgroup.find(".scope", id_start); auto id = cgroup.substr(id_start, id_end - id_start); // As of BPM v1.0.3, the container ID is only allowed to contain the following chars // see https://github.com/cloudfoundry-incubator/bpm-release/blob/v1.0.3/src/bpm/jobid/encoding.go if (!id.empty() && strspn(id.c_str(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._-") == id.size()) { container_info.m_type = CT_BPM; container_info.m_id = id; matches = true; break; } } } if (!matches) { return false; } tinfo->m_container_id = container_info.m_id; if (!manager->container_exists(container_info.m_id)) { container_info.m_name = container_info.m_id; manager->add_container(container_info, tinfo); manager->notify_new_container(container_info); } return true; } sysdig-0.26.4/userspace/libsinsp/container_engine/bpm.h000066400000000000000000000017061352731327100231700ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once class sinsp_container_manager; class sinsp_container_info; class sinsp_threadinfo; #include "container_engine/container_engine.h" namespace libsinsp { namespace container_engine { class bpm : public resolver { public: bool resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) override; }; } } sysdig-0.26.4/userspace/libsinsp/container_engine/container_engine.cpp000066400000000000000000000013341352731327100262510ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "container_engine/container_engine.h" using namespace libsinsp::container_engine; void resolver::cleanup() { } sysdig-0.26.4/userspace/libsinsp/container_engine/container_engine.h000066400000000000000000000016551352731327100257240ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once class sinsp_container_manager; class sinsp_threadinfo; namespace libsinsp { namespace container_engine { class resolver { public: virtual ~resolver() = default; virtual bool resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) = 0; virtual void cleanup(); }; } } sysdig-0.26.4/userspace/libsinsp/container_engine/cri.cpp000066400000000000000000000154471352731327100235310ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "container_engine/cri.h" #include #ifdef GRPC_INCLUDE_IS_GRPCPP # include #else # include #endif #include "cri.pb.h" #include "cri.grpc.pb.h" #include "runc.h" #include "container_engine/mesos.h" #include "grpc_channel_registry.h" #include #include "sinsp.h" #include "sinsp_int.h" using namespace libsinsp::cri; using namespace libsinsp::container_engine; using namespace libsinsp::runc; namespace { bool parse_containerd(const runtime::v1alpha2::ContainerStatusResponse& status, sinsp_container_info *container, sinsp_threadinfo *tinfo) { const auto &info_it = status.info().find("info"); if(info_it == status.info().end()) { return false; } Json::Value root; Json::Reader reader; if(!reader.parse(info_it->second, root)) { ASSERT(false); return false; } parse_cri_env(root, container); parse_cri_json_image(root, container); parse_cri_runtime_spec(root, container); if(root.isMember("sandboxID") && root["sandboxID"].isString()) { const auto pod_sandbox_id = root["sandboxID"].asString(); container->m_container_ip = ntohl(get_pod_sandbox_ip(pod_sandbox_id)); } return true; } bool parse_cri(sinsp_container_manager *manager, sinsp_container_info *container, sinsp_threadinfo *tinfo) { if(!s_cri) { // This isn't an error in the case where the // configured unix domain socket doesn't exist. In // that case, s_cri isn't initialized at all. Hence, // the DEBUG. g_logger.format(sinsp_logger::SEV_DEBUG, "cri (%s): Could not parse cri (no s_cri object)", container->m_id.c_str()); return false; } runtime::v1alpha2::ContainerStatusRequest req; runtime::v1alpha2::ContainerStatusResponse resp; req.set_container_id(container->m_id); req.set_verbose(true); grpc::ClientContext context; auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(s_cri_timeout); context.set_deadline(deadline); grpc::Status status = s_cri->ContainerStatus(&context, req, &resp); g_logger.format(sinsp_logger::SEV_DEBUG, "cri (%s): Status from ContainerStatus: (%s)", container->m_id.c_str(), status.error_message().c_str()); if(!status.ok()) { if(is_pod_sandbox(container->m_id)) { container->m_is_pod_sandbox = true; return true; } g_logger.format(sinsp_logger::SEV_DEBUG, "cri (%s): id is neither a container nor a pod sandbox", container->m_id.c_str()); return false; } if(!resp.has_status()) { ASSERT(false); return false; } const auto &resp_container = resp.status(); container->m_name = resp_container.metadata().name(); for(const auto &pair : resp_container.labels()) { container->m_labels[pair.first] = pair.second; } parse_cri_image(resp_container, container); parse_cri_mounts(resp_container, container); if(parse_containerd(resp, container, tinfo)) { return true; } if(s_cri_extra_queries) { container->m_container_ip = get_container_ip(container->m_id); container->m_imageid = get_container_image_id(resp_container.image_ref()); } return true; } constexpr const cgroup_layout CRI_CGROUP_LAYOUT[] = { {"/", ""}, // non-systemd containerd {"/crio-", ""}, // non-systemd cri-o {"/cri-containerd-", ".scope"}, // systemd containerd {"/crio-", ".scope"}, // systemd cri-o {nullptr, nullptr} }; } cri::cri() { if(s_cri || s_cri_unix_socket_path.empty()) { return; } auto cri_path = scap_get_host_root() + s_cri_unix_socket_path; struct stat s = {}; if(stat(cri_path.c_str(), &s) != 0 || (s.st_mode & S_IFMT) != S_IFSOCK) { return; } std::shared_ptr channel = libsinsp::grpc_channel_registry::get_channel("unix://" + cri_path); s_cri = runtime::v1alpha2::RuntimeService::NewStub(channel); s_cri_image = runtime::v1alpha2::ImageService::NewStub(channel); runtime::v1alpha2::VersionRequest vreq; runtime::v1alpha2::VersionResponse vresp; vreq.set_version("v1alpha2"); grpc::ClientContext context; auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(s_cri_timeout); context.set_deadline(deadline); grpc::Status status = s_cri->Version(&context, vreq, &vresp); if (!status.ok()) { g_logger.format(sinsp_logger::SEV_NOTICE, "cri: CRI runtime returned an error after version check at %s: %s", s_cri_unix_socket_path.c_str(), status.error_message().c_str()); s_cri.reset(nullptr); s_cri_unix_socket_path = ""; return; } g_logger.format(sinsp_logger::SEV_INFO, "cri: CRI runtime: %s %s", vresp.runtime_name().c_str(), vresp.runtime_version().c_str()); s_cri_runtime_type = get_cri_runtime_type(vresp.runtime_name()); } void cri::cleanup() { s_cri.reset(nullptr); s_cri_image.reset(nullptr); s_cri_extra_queries = true; } void cri::set_cri_socket_path(const std::string& path) { s_cri_unix_socket_path = path; } void cri::set_cri_timeout(int64_t timeout_ms) { s_cri_timeout = timeout_ms; } void cri::set_extra_queries(bool extra_queries) { s_cri_extra_queries = extra_queries; } bool cri::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) { sinsp_container_info container_info; sinsp_container_info *existing_container_info; if(!matches_runc_cgroups(tinfo, CRI_CGROUP_LAYOUT, container_info.m_id)) { return false; } tinfo->m_container_id = container_info.m_id; existing_container_info = manager->get_container(container_info.m_id); if (!existing_container_info || existing_container_info->m_metadata_complete == false) { if (query_os_for_missing_info) { g_logger.format(sinsp_logger::SEV_DEBUG, "cri (%s): Performing lookup", container_info.m_id.c_str()); if (!parse_cri(manager, &container_info, tinfo)) { g_logger.format(sinsp_logger::SEV_DEBUG, "cri (%s): Failed to get CRI metadata for container", container_info.m_id.c_str()); return false; } // If here, parse_cri succeeded so we can // assign an actual type. container_info.m_type = s_cri_runtime_type; } if (mesos::set_mesos_task_id(&container_info, tinfo)) { g_logger.format(sinsp_logger::SEV_DEBUG, "cri (%s) Mesos CRI container, Mesos task ID: [%s]", container_info.m_id.c_str(), container_info.m_mesos_task_id.c_str()); } manager->add_container(container_info, tinfo); manager->notify_new_container(container_info); } return true; } sysdig-0.26.4/userspace/libsinsp/container_engine/cri.h000066400000000000000000000022251352731327100231640ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #include class sinsp_container_manager; class sinsp_threadinfo; #include "container_engine/container_engine.h" namespace libsinsp { namespace container_engine { class cri : public resolver { public: cri(); bool resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) override; void cleanup() override; static void set_cri_socket_path(const std::string& path); static void set_cri_timeout(int64_t timeout_ms); static void set_extra_queries(bool extra_queries); }; } } sysdig-0.26.4/userspace/libsinsp/container_engine/docker.h000066400000000000000000000074351352731327100236660ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #include #include #include #if !defined(_WIN32) #include #include #include #endif #include "json/json.h" #include "async_key_value_source.h" #include "container_info.h" #include "container_engine/container_engine.h" class sinsp; class sinsp_container_manager; class sinsp_container_info; class sinsp_threadinfo; namespace libsinsp { namespace container_engine { struct container_lookup_result { bool m_successful; sinsp_container_info m_container_info; }; class docker_async_source : public sysdig::async_key_value_source { enum docker_response { RESP_OK = 0, RESP_BAD_REQUEST = 1, RESP_ERROR = 2 }; public: docker_async_source(uint64_t max_wait_ms, uint64_t ttl_ms, sinsp *inspector); virtual ~docker_async_source(); static void set_query_image_info(bool query_image_info); protected: void run_impl(); private: // These 4 methods are OS-dependent and defined in docker_{linux,win}.cpp void init_docker_conn(); void free_docker_conn(); std::string build_request(const std::string& url); docker_response get_docker(const std::string& url, std::string &json); bool parse_docker(std::string &container_id, sinsp_container_info *container); // Look for a pod specification in this container's labels and // if found set spec to the pod spec. bool get_k8s_pod_spec(const Json::Value &config_obj, Json::Value &spec); std::string normalize_arg(const std::string &arg); // Parse a healthcheck out of the provided healthcheck object, // updating the container info with any healthcheck found. void parse_healthcheck(const Json::Value &healthcheck_obj, sinsp_container_info *container); // Parse either a readiness or liveness probe out of the // provided object, updating the container info with any probe // found. bool parse_liveness_readiness_probe(const Json::Value &probe_obj, sinsp_container_info::container_health_probe::probe_type ptype, sinsp_container_info *container); // Parse all healthchecks/liveness probes/readiness probes out // of the provided object, updating the container info as required. void parse_health_probes(const Json::Value &config_obj, sinsp_container_info *container); sinsp *m_inspector; std::string m_docker_unix_socket_path; std::string m_api_version; #ifndef _WIN32 CURLM *m_curlm; CURL *m_curl; #endif static bool m_query_image_info; }; class docker : public resolver { public: docker(); bool resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) override; void cleanup() override; static void parse_json_mounts(const Json::Value &mnt_obj, std::vector &mounts); // Container name only set for windows. For linux name must be fetched via lookup static bool detect_docker(const sinsp_threadinfo* tinfo, std::string& container_id, std::string &container_name); protected: void parse_docker_async(sinsp *inspector, std::string &container_id, sinsp_container_manager *manager); std::unique_ptr m_docker_info_source; static std::string s_incomplete_info_name; }; } } sysdig-0.26.4/userspace/libsinsp/container_engine/docker_common.cpp000066400000000000000000000537011352731327100255660ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "container_engine/docker.h" #include "cgroup_list_counter.h" #include "sinsp.h" #include "sinsp_int.h" #include "container.h" #include "utils.h" #include using namespace libsinsp::container_engine; docker_async_source::docker_async_source(uint64_t max_wait_ms, uint64_t ttl_ms, sinsp *inspector) : async_key_value_source(max_wait_ms, ttl_ms), m_inspector(inspector), m_docker_unix_socket_path("/var/run/docker.sock"), #ifdef _WIN32 m_api_version("/v1.30"), #else m_api_version("/v1.24"), m_curlm(NULL), m_curl(NULL) #endif { init_docker_conn(); } docker_async_source::~docker_async_source() { this->stop(); g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async: Source destructor"); free_docker_conn(); } void docker_async_source::run_impl() { std::string container_id; while (dequeue_next_key(container_id)) { g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): Source dequeued key", container_id.c_str()); container_lookup_result res; res.m_successful = true; res.m_container_info.m_type = CT_DOCKER; res.m_container_info.m_id = container_id; if(!parse_docker(container_id, &res.m_container_info)) { // This is not always an error e.g. when using // containerd as the runtime. Since the cgroup // names are often identical between // containerd and docker, we have to try to // fetch both. g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): Failed to get Docker metadata, returning successful=false", container_id.c_str()); res.m_successful = false; } g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): Parse successful, storing value", container_id.c_str()); // Return a result object either way, to ensure any // new container callbacks are called. store_value(container_id, res); } } bool docker_async_source::m_query_image_info = true; void docker::parse_json_mounts(const Json::Value &mnt_obj, vector &mounts) { if(!mnt_obj.isNull() && mnt_obj.isArray()) { for(uint32_t i=0; im_id.c_str(), Json::FastWriter().write(healthcheck_obj).c_str()); if(healthcheck_obj.isNull()) { g_logger.format(sinsp_logger::SEV_WARNING, "Could not parse health check from %s (No Healthcheck property)", Json::FastWriter().write(healthcheck_obj).c_str()); return; } if(!healthcheck_obj.isMember("Test")) { g_logger.format(sinsp_logger::SEV_WARNING, "Could not parse health check from %s (Healthcheck does not have Test property)", Json::FastWriter().write(healthcheck_obj).c_str()); return; } const Json::Value &test_obj = healthcheck_obj["Test"]; if(!test_obj.isArray()) { g_logger.format(sinsp_logger::SEV_WARNING, "Could not parse health check from %s (Healthcheck Test property is not array)", Json::FastWriter().write(healthcheck_obj).c_str()); return; } if(test_obj.size() == 1) { if(test_obj[0].asString() != "NONE") { g_logger.format(sinsp_logger::SEV_WARNING, "Could not parse health check from %s (Expected NONE for single-element Test array)", Json::FastWriter().write(healthcheck_obj).c_str()); } return; } if(test_obj[0].asString() == "CMD") { std::string exe = normalize_arg(test_obj[1].asString()); std::vector args; for(uint32_t i = 2; i < test_obj.size(); i++) { args.push_back(normalize_arg(test_obj[i].asString())); } g_logger.format(sinsp_logger::SEV_DEBUG, "docker (%s): Setting PT_HEALTHCHECK exe=%s nargs=%d", container->m_id.c_str(), exe.c_str(), args.size()); container->m_health_probes.emplace_back(sinsp_container_info::container_health_probe::PT_HEALTHCHECK, std::move(exe), std::move(args)); } else if(test_obj[0].asString() == "CMD-SHELL") { std::string exe = "/bin/sh"; std::vector args; args.push_back("-c"); args.push_back(test_obj[1].asString()); g_logger.format(sinsp_logger::SEV_DEBUG, "docker (%s): Setting PT_HEALTHCHECK exe=%s nargs=%d", container->m_id.c_str(), exe.c_str(), args.size()); container->m_health_probes.emplace_back(sinsp_container_info::container_health_probe::PT_HEALTHCHECK, std::move(exe), std::move(args)); } else { g_logger.format(sinsp_logger::SEV_WARNING, "Could not parse health check from %s (Expected CMD/CMD-SHELL for multi-element Test array)", Json::FastWriter().write(healthcheck_obj).c_str()); return; } } bool docker_async_source::parse_liveness_readiness_probe(const Json::Value &probe_obj, sinsp_container_info::container_health_probe::probe_type ptype, sinsp_container_info *container) { if(probe_obj.isNull() || !probe_obj.isMember("exec") || !probe_obj["exec"].isMember("command")) { g_logger.format(sinsp_logger::SEV_WARNING, "Could not parse liveness/readiness probe from %s", Json::FastWriter().write(probe_obj).c_str()); return false; } const Json::Value command_obj = probe_obj["exec"]["command"]; if(!command_obj.isNull() && command_obj.isArray()) { std::string exe; std::vector args; exe = normalize_arg(command_obj[0].asString()); for(uint32_t i = 1; i < command_obj.size(); i++) { args.push_back(normalize_arg(command_obj[i].asString())); } g_logger.format(sinsp_logger::SEV_DEBUG, "docker (%s): Setting %s exe=%s nargs=%d", container->m_id.c_str(), sinsp_container_info::container_health_probe::probe_type_names[ptype].c_str(), exe.c_str(), args.size()); container->m_health_probes.emplace_back(ptype, std::move(exe), std::move(args)); } return true; } void docker_async_source::parse_health_probes(const Json::Value &config_obj, sinsp_container_info *container) { Json::Value spec; bool liveness_readiness_added = false; // When parsing the full container json for live containers, a label contains stringified json that // contains the probes. if (get_k8s_pod_spec(config_obj, spec)) { if(spec.isMember("livenessProbe")) { if(parse_liveness_readiness_probe(spec["livenessProbe"], sinsp_container_info::container_health_probe::PT_LIVENESS_PROBE, container)) { liveness_readiness_added = true; } } else if(spec.isMember("readinessProbe")) { if(parse_liveness_readiness_probe(spec["readinessProbe"], sinsp_container_info::container_health_probe::PT_READINESS_PROBE, container)) { liveness_readiness_added = true; } } } // To avoid any confusion about containers that both refer to // a healthcheck and liveness/readiness probe, we only // consider a healthcheck if no liveness/readiness was added. if(!liveness_readiness_added && config_obj.isMember("Healthcheck")) { parse_healthcheck(config_obj["Healthcheck"], container); } } void docker_async_source::set_query_image_info(bool query_image_info) { g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async: Setting query_image_info=%s", (query_image_info ? "true" : "false")); m_query_image_info = query_image_info; } std::string docker::s_incomplete_info_name = "incomplete"; bool docker::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) { std::string container_id, container_name; sinsp_container_info *existing_container_info; if(!detect_docker(tinfo, container_id, container_name)) { return false; } if(!m_docker_info_source) { g_logger.log("docker_async: Creating docker async source", sinsp_logger::SEV_DEBUG); uint64_t max_wait_ms = 10000; docker_async_source *src = new docker_async_source(docker_async_source::NO_WAIT_LOOKUP, max_wait_ms, manager->get_inspector()); m_docker_info_source.reset(src); } tinfo->m_container_id = container_id; existing_container_info = manager->get_container(container_id); if(!existing_container_info) { // Add a minimal container_info object where only the // container id, (possibly) name, and a container // image = incomplete is filled in. This may be // overidden later once parse_docker_async completes. sinsp_container_info container_info; g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): No existing container info, creating initial stub info", container_id.c_str()); container_info.m_type = CT_DOCKER; container_info.m_id = container_id; container_info.m_name = container_name; container_info.m_image = s_incomplete_info_name; container_info.m_imageid = s_incomplete_info_name; container_info.m_imagerepo = s_incomplete_info_name; container_info.m_imagetag = s_incomplete_info_name; container_info.m_imagedigest = s_incomplete_info_name; container_info.m_metadata_complete = false; manager->add_container(container_info, tinfo); existing_container_info = manager->get_container(container_id); } #ifdef HAS_CAPTURE // Possibly start a lookup for this container info if(!existing_container_info->m_metadata_complete && query_os_for_missing_info) { // give docker a chance to return metadata for this container parse_docker_async(manager->get_inspector(), container_id, manager); } #endif // Returning true will prevent other container engines from // trying to resolve the container, so only return true if we // have complete metadata. return existing_container_info->m_metadata_complete; } void docker::parse_docker_async(sinsp *inspector, std::string &container_id, sinsp_container_manager *manager) { auto cb = [manager](const std::string &container_id, const container_lookup_result &res) { g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): Source callback result successful=%s", container_id.c_str(), (res.m_successful ? "true" : "false")); if(res.m_successful) { manager->notify_new_container(res.m_container_info); } }; container_lookup_result result; if (m_docker_info_source->lookup(container_id, result, cb)) { // if a previous lookup call already found the metadata, process it now cb(container_id, result); // This should *never* happen, as ttl is 0 (never wait) g_logger.format(sinsp_logger::SEV_ERROR, "docker_async (%s): Unexpected immediate return from docker_info_source.lookup()", container_id.c_str()); } } bool docker_async_source::parse_docker(std::string &container_id, sinsp_container_info *container) { string json; g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): Looking up info for container", container_id.c_str()); docker_response resp = get_docker(build_request("/containers/" + container_id + "/json"), json); switch(resp) { case docker_response::RESP_BAD_REQUEST: g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): Initial url fetch failed, trying w/o api version", container_id.c_str()); m_api_version = ""; json = ""; resp = get_docker(build_request("/containers/" + container_id + "/json"), json); if (resp == docker_response::RESP_OK) { break; } /* FALLTHRU */ case docker_response::RESP_ERROR: g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): Url fetch failed, returning false", container_id.c_str()); return false; case docker_response::RESP_OK: break; } g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): Parsing containers response \"%s\"", container_id.c_str(), json.c_str()); Json::Value root; Json::Reader reader; bool parsingSuccessful = reader.parse(json, root); if(!parsingSuccessful) { g_logger.format(sinsp_logger::SEV_ERROR, "docker_async (%s): Could not parse json \"%s\", returning false", container_id.c_str(), json.c_str()); ASSERT(false); return false; } const Json::Value& config_obj = root["Config"]; container->m_image = config_obj["Image"].asString(); string imgstr = root["Image"].asString(); size_t cpos = imgstr.find(":"); if(cpos != string::npos) { container->m_imageid = imgstr.substr(cpos + 1); } parse_health_probes(config_obj, container); // containers can be spawned using just the imageID as image name, // with or without the hash prefix (e.g. sha256:) bool no_name = !container->m_imageid.empty() && strncmp(container->m_image.c_str(), container->m_imageid.c_str(), MIN(container->m_image.length(), container->m_imageid.length())) == 0; no_name |= !imgstr.empty() && strncmp(container->m_image.c_str(), imgstr.c_str(), MIN(container->m_image.length(), imgstr.length())) == 0; if(!no_name || !m_query_image_info) { string hostname, port; sinsp_utils::split_container_image(container->m_image, hostname, port, container->m_imagerepo, container->m_imagetag, container->m_imagedigest, false); } if(m_query_image_info && !container->m_imageid.empty() && (no_name || container->m_imagedigest.empty() || (!container->m_imagedigest.empty() && container->m_imagetag.empty()))) { g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s) image (%s): Fetching image info", container_id.c_str(), container->m_imageid.c_str()); string img_json; if(get_docker(build_request("/images/" + container->m_imageid + "/json?digests=1"), img_json) == docker_response::RESP_OK) { g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s) image (%s): Image info fetch returned \"%s\"", container_id.c_str(), container->m_imageid.c_str(), img_json.c_str()); Json::Value img_root; if(reader.parse(img_json, img_root)) { // img_root["RepoDigests"] contains only digests for images pulled from registries. // If an image gets retagged and is never pushed to any registry, we will not find // that entry in container->m_imagerepo. Also, for locally built images we have the // same issue. This leads to container->m_imagedigest being empty as well. unordered_set imageDigestSet; for(const auto& rdig : img_root["RepoDigests"]) { if(rdig.isString()) { string repodigest = rdig.asString(); string digest = repodigest.substr(repodigest.find('@')+1); imageDigestSet.insert(digest); if(container->m_imagerepo.empty()) { container->m_imagerepo = repodigest.substr(0, repodigest.find('@')); } if(repodigest.find(container->m_imagerepo) != string::npos) { container->m_imagedigest = digest; break; } } } for(const auto& rtag : img_root["RepoTags"]) { if(rtag.isString()) { string repotag = rtag.asString(); if(container->m_imagerepo.empty()) { container->m_imagerepo = repotag.substr(0, repotag.rfind(":")); } if(repotag.find(container->m_imagerepo) != string::npos) { container->m_imagetag = repotag.substr(repotag.rfind(":")+1); break; } } } // fix image digest for locally tagged images or multiple repo digests. // Case 1: One repo digest with many tags. // Case 2: Many repo digests with the same digest value. if(container->m_imagedigest.empty() && imageDigestSet.size() == 1) { container->m_imagedigest = *imageDigestSet.begin(); } } else { g_logger.format(sinsp_logger::SEV_ERROR, "docker_async (%s) image (%s): Could not parse json image info \"%s\"", container_id.c_str(), container->m_imageid.c_str(), img_json.c_str()); } } else { g_logger.format(sinsp_logger::SEV_ERROR, "docker_async (%s) image (%s): Could not fetch image info", container_id.c_str(), container->m_imageid.c_str()); } } if(container->m_imagetag.empty()) { container->m_imagetag = "latest"; } container->m_name = root["Name"].asString(); // k8s Docker container names could have '/' as the first character. if(!container->m_name.empty() && container->m_name[0] == '/') { container->m_name = container->m_name.substr(1); } if(container->m_name.find("k8s_POD") == 0) { container->m_is_pod_sandbox = true; } const Json::Value& net_obj = root["NetworkSettings"]; string ip = net_obj["IPAddress"].asString(); if(ip.empty()) { const Json::Value& hconfig_obj = root["HostConfig"]; string net_mode = hconfig_obj["NetworkMode"].asString(); if(strncmp(net_mode.c_str(), "container:", strlen("container:")) == 0) { std::string secondary_container_id = net_mode.substr(net_mode.find(":") + 1); sinsp_container_info pcnt; pcnt.m_id = secondary_container_id; // This is a *blocking* fetch of the // secondary container, but we're in a // separate thread so this is ok. g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s), secondary (%s): Doing blocking fetch of secondary container", container_id.c_str(), secondary_container_id.c_str()); if (parse_docker(secondary_container_id, &pcnt)) { g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s), secondary (%s): Secondary fetch successful", container_id.c_str(), secondary_container_id.c_str()); container->m_container_ip = pcnt.m_container_ip; } else { g_logger.format(sinsp_logger::SEV_ERROR, "docker_async (%s), secondary (%s): Secondary fetch failed", container_id.c_str(), secondary_container_id.c_str()); } } } else { if(inet_pton(AF_INET, ip.c_str(), &container->m_container_ip) == -1) { ASSERT(false); } container->m_container_ip = ntohl(container->m_container_ip); } vector ports = net_obj["Ports"].getMemberNames(); for(vector::const_iterator it = ports.begin(); it != ports.end(); ++it) { size_t tcp_pos = it->find("/tcp"); if(tcp_pos == string::npos) { continue; } uint16_t container_port = atoi(it->c_str()); const Json::Value& v = net_obj["Ports"][*it]; if(v.isArray()) { for(uint32_t j = 0; j < v.size(); ++j) { sinsp_container_info::container_port_mapping port_mapping; ip = v[j]["HostIp"].asString(); string port = v[j]["HostPort"].asString(); if(inet_pton(AF_INET, ip.c_str(), &port_mapping.m_host_ip) == -1) { ASSERT(false); continue; } port_mapping.m_host_ip = ntohl(port_mapping.m_host_ip); port_mapping.m_container_port = container_port; port_mapping.m_host_port = atoi(port.c_str()); container->m_port_mappings.push_back(port_mapping); } } } vector labels = config_obj["Labels"].getMemberNames(); for(vector::const_iterator it = labels.begin(); it != labels.end(); ++it) { string val = config_obj["Labels"][*it].asString(); container->m_labels[*it] = val; } const Json::Value& env_vars = config_obj["Env"]; for(const auto& env_var : env_vars) { if(env_var.isString()) { container->m_env.emplace_back(env_var.asString()); } } const auto& host_config_obj = root["HostConfig"]; container->m_memory_limit = host_config_obj["Memory"].asInt64(); container->m_swap_limit = host_config_obj["MemorySwap"].asInt64(); const auto cpu_shares = host_config_obj["CpuShares"].asInt64(); if(cpu_shares > 0) { container->m_cpu_shares = cpu_shares; } container->m_cpu_quota = host_config_obj["CpuQuota"].asInt64(); const auto cpu_period = host_config_obj["CpuPeriod"].asInt64(); if(cpu_period > 0) { container->m_cpu_period = cpu_period; } const auto cpuset_cpus = host_config_obj["CpusetCpus"].asString(); if (!cpuset_cpus.empty()) { libsinsp::cgroup_list_counter counter; container->m_cpuset_cpu_count = counter(cpuset_cpus.c_str(), sinsp_logger::SEV_DEBUG); } const Json::Value& privileged = host_config_obj["Privileged"]; if(!privileged.isNull() && privileged.isBool()) { container->m_privileged = privileged.asBool(); } docker::parse_json_mounts(root["Mounts"], container->m_mounts); #ifdef HAS_ANALYZER sinsp_utils::find_env(container->m_sysdig_agent_conf, container->get_env(), "SYSDIG_AGENT_CONF"); // container->m_sysdig_agent_conf = get_docker_env(env_vars, "SYSDIG_AGENT_CONF"); #endif g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): parse_docker returning true", container_id.c_str()); return true; } sysdig-0.26.4/userspace/libsinsp/container_engine/docker_linux.cpp000066400000000000000000000124661352731327100254400ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "container_engine/docker.h" #include "runc.h" #include "container_engine/mesos.h" #include "sinsp.h" #include "sinsp_int.h" using namespace libsinsp::container_engine; using namespace libsinsp::runc; namespace { size_t docker_curl_write_callback(const char* ptr, size_t size, size_t nmemb, string* json) { const std::size_t total = size * nmemb; json->append(ptr, total); return total; } constexpr const cgroup_layout DOCKER_CGROUP_LAYOUT[] = { {"/", ""}, // non-systemd docker {"/docker-", ".scope"}, // systemd docker {nullptr, nullptr} }; } docker::docker() { } void docker::cleanup() { m_docker_info_source.reset(NULL); } void docker_async_source::init_docker_conn() { if(!m_curlm) { m_curl = curl_easy_init(); m_curlm = curl_multi_init(); if(m_curlm) { curl_multi_setopt(m_curlm, CURLMOPT_PIPELINING, CURLPIPE_HTTP1|CURLPIPE_MULTIPLEX); } if(m_curl) { auto docker_path = scap_get_host_root() + m_docker_unix_socket_path; curl_easy_setopt(m_curl, CURLOPT_UNIX_SOCKET_PATH, docker_path.c_str()); curl_easy_setopt(m_curl, CURLOPT_HTTPGET, 1); curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, docker_curl_write_callback); } } } void docker_async_source::free_docker_conn() { if(m_curl) { curl_easy_cleanup(m_curl); m_curl = NULL; } if(m_curlm) { curl_multi_cleanup(m_curlm); m_curlm = NULL; } } std::string docker_async_source::build_request(const std::string &url) { return "http://localhost" + m_api_version + url; } docker_async_source::docker_response docker_async_source::get_docker(const std::string& url, std::string &json) { g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): Fetching url", url.c_str()); if(curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str()) != CURLE_OK) { g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): curl_easy_setopt(CURLOPT_URL) failed", url.c_str()); ASSERT(false); return docker_response::RESP_ERROR; } if(curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &json) != CURLE_OK) { g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): curl_easy_setopt(CURLOPT_WRITEDATA) failed", url.c_str()); ASSERT(false); return docker_response::RESP_ERROR; } if(curl_multi_add_handle(m_curlm, m_curl) != CURLM_OK) { g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): curl_multi_add_handle() failed", url.c_str()); ASSERT(false); return docker_response::RESP_ERROR; } while(true) { int still_running; CURLMcode res = curl_multi_perform(m_curlm, &still_running); if(res != CURLM_OK) { g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): curl_multi_perform() failed", url.c_str()); ASSERT(false); return docker_response::RESP_ERROR; } if(still_running == 0) { break; } int numfds; res = curl_multi_wait(m_curlm, NULL, 0, -1, &numfds); if(res != CURLM_OK) { g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): curl_multi_wait() failed", url.c_str()); ASSERT(false); return docker_response::RESP_ERROR; } } if(curl_multi_remove_handle(m_curlm, m_curl) != CURLM_OK) { g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): curl_multi_remove_handle() failed", url.c_str()); ASSERT(false); return docker_response::RESP_ERROR; } long http_code = 0; if(curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &http_code) != CURLE_OK) { g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): curl_easy_getinfo(CURLINFO_RESPONSE_CODE) failed", url.c_str()); ASSERT(false); return docker_response::RESP_ERROR; } g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): http_code=%ld", url.c_str(), http_code); switch(http_code) { case 0: /* connection failed, apparently */ g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): returning RESP_ERROR", url.c_str()); return docker_response::RESP_ERROR; case 200: g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): returning RESP_OK", url.c_str()); return docker_response::RESP_OK; default: g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): returning RESP_BAD_REQUEST", url.c_str()); return docker_response::RESP_BAD_REQUEST; } g_logger.format(sinsp_logger::SEV_DEBUG, "docker_async (%s): fallthrough, returning RESP_OK", url.c_str()); return docker_response::RESP_OK; } bool docker::detect_docker(const sinsp_threadinfo *tinfo, std::string &container_id, std::string &container_name) { if(matches_runc_cgroups(tinfo, DOCKER_CGROUP_LAYOUT, container_id)) { // The container name is only available in windows container_name = s_incomplete_info_name; return true; } return false; } sysdig-0.26.4/userspace/libsinsp/container_engine/docker_win.cpp000066400000000000000000000041311352731327100250640ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "container_engine/docker.h" #include "sinsp.h" #include "sinsp_int.h" #include "dragent_win_hal_public.h" using namespace libsinsp::container_engine; docker::docker() { } void docker::cleanup() { g_docker_info_source.reset(NULL); } void docker_async_source::init_docker_conn() { } void docker_async_source::free_docker_conn() { } std::string docker_async_source::build_request(const std::string &url) { return "GET " + m_api_version + url + " HTTP/1.1\r\nHost: docker\r\n\r\n"; } bool docker::detect_docker(sinsp_threadinfo *tinfo, std::string &container_id, std::string &container_name) { wh_docker_container_info wcinfo = wh_docker_resolve_pid(manager->get_inspector()->get_wmi_handle(), tinfo->m_pid); if(!wcinfo.m_res) { return false; } container_id = wcinfo.m_container_id; container_name = wcinfo.m_container_name; return true; } docker_async_source::docker_response docker_async_source::get_docker(const std::string& url, std::string &json) { const char* response = NULL; bool qdres = wh_query_docker(m_inspector->get_wmi_handle(), (char*)url.c_str(), &response); if(qdres == false) { ASSERT(false); return docker_response::RESP_ERROR; } json = response; if(strncmp(json.c_str(), "HTTP/1.0 200 OK", sizeof("HTTP/1.0 200 OK") -1)) { return docker_response::RESP_BAD_REQUEST; } size_t pos = json.find("{"); if(pos == string::npos) { ASSERT(false); return docker_response::RESP_ERROR; } json = json.substr(pos); return docker_response::RESP_OK; } sysdig-0.26.4/userspace/libsinsp/container_engine/libvirt_lxc.cpp000066400000000000000000000046151352731327100252700ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "container_engine/libvirt_lxc.h" #include "sinsp.h" using namespace libsinsp::container_engine; bool libvirt_lxc::match(sinsp_threadinfo* tinfo, sinsp_container_info* container_info) { for(const auto& it : tinfo->m_cgroups) { // // Non-systemd libvirt-lxc // const auto& cgroup = it.second; size_t pos = cgroup.find(".libvirt-lxc"); if(pos != std::string::npos && pos == cgroup.length() - sizeof(".libvirt-lxc") + 1) { size_t pos2 = cgroup.find_last_of("/"); if(pos2 != std::string::npos) { container_info->m_type = CT_LIBVIRT_LXC; container_info->m_id = cgroup.substr(pos2 + 1, pos - pos2 - 1); return true; } } // // systemd libvirt-lxc // pos = cgroup.find("-lxc\\x2"); if(pos != std::string::npos) { size_t pos2 = cgroup.find(".scope"); if(pos2 != std::string::npos && pos2 == cgroup.length() - sizeof(".scope") + 1) { container_info->m_type = CT_LIBVIRT_LXC; container_info->m_id = cgroup.substr(pos + sizeof("-lxc\\x2"), pos2 - pos - sizeof("-lxc\\x2")); return true; } } // // Legacy libvirt-lxc // pos = cgroup.find("/libvirt/lxc/"); if(pos != std::string::npos) { container_info->m_type = CT_LIBVIRT_LXC; container_info->m_id = cgroup.substr(pos + sizeof("/libvirt/lxc/") - 1); return true; } } return false; } bool libvirt_lxc::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) { sinsp_container_info container_info; if (!match(tinfo, &container_info)) { return false; } tinfo->m_container_id = container_info.m_id; if (!manager->container_exists(container_info.m_id)) { container_info.m_name = container_info.m_id; manager->add_container(container_info, tinfo); manager->notify_new_container(container_info); } return true; } sysdig-0.26.4/userspace/libsinsp/container_engine/libvirt_lxc.h000066400000000000000000000020451352731327100247300ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once class sinsp_container_manager; class sinsp_container_info; class sinsp_threadinfo; #include "container_engine/container_engine.h" namespace libsinsp { namespace container_engine { class libvirt_lxc : public resolver { public: bool resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) override; protected: bool match(sinsp_threadinfo* tinfo, sinsp_container_info* container_info); }; } } sysdig-0.26.4/userspace/libsinsp/container_engine/lxc.cpp000066400000000000000000000035651352731327100235400ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "container_engine/lxc.h" #include "sinsp.h" using namespace libsinsp::container_engine; bool lxc::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) { sinsp_container_info container_info; bool matches = false; for(const auto& it : tinfo->m_cgroups) { // // Non-systemd LXC // const auto& cgroup = it.second; size_t pos = cgroup.find("/lxc/"); if(pos != std::string::npos) { auto id_start = pos + sizeof("/lxc/") - 1; auto id_end = cgroup.find('/', id_start); container_info.m_type = CT_LXC; container_info.m_id = cgroup.substr(id_start, id_end - id_start); matches = true; break; } pos = cgroup.find("/lxc.payload/"); if(pos != std::string::npos) { auto id_start = pos + sizeof("/lxc.payload/") - 1; auto id_end = cgroup.find('/', id_start); container_info.m_type = CT_LXC; container_info.m_id = cgroup.substr(id_start, id_end - id_start); matches = true; break; } } if (!matches) { return false; } tinfo->m_container_id = container_info.m_id; if (!manager->container_exists(container_info.m_id)) { container_info.m_name = container_info.m_id; manager->add_container(container_info, tinfo); manager->notify_new_container(container_info); } return true; } sysdig-0.26.4/userspace/libsinsp/container_engine/lxc.h000066400000000000000000000017061352731327100232000ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once class sinsp_container_manager; class sinsp_container_info; class sinsp_threadinfo; #include "container_engine/container_engine.h" namespace libsinsp { namespace container_engine { class lxc : public resolver { public: bool resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) override; }; } } sysdig-0.26.4/userspace/libsinsp/container_engine/mesos.cpp000066400000000000000000000110201352731327100240610ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "container_engine/mesos.h" #include #include "sinsp.h" #include "sinsp_int.h" bool libsinsp::container_engine::mesos::match(sinsp_threadinfo* tinfo, sinsp_container_info* container_info) { for(auto it = tinfo->m_cgroups.begin(); it != tinfo->m_cgroups.end(); ++it) { string cgroup = it->second; size_t pos; pos = cgroup.find("/mesos/"); if(pos != string::npos) { // It should match `/mesos/a9f41620-b165-4d24-abe0-af0af92e7b20` auto id = cgroup.substr(pos + sizeof("/mesos/") - 1); if(id.size() == 36 && id.find_first_not_of("0123456789abcdefABCDEF-") == string::npos) { container_info->m_type = CT_MESOS; container_info->m_id = move(id); // Consider a mesos container valid only if we find the mesos_task_id // this will exclude from the container itself the mesos-executor // but makes sure that we have task_id parsed properly. Otherwise what happens // is that we'll create a mesos container struct without a mesos_task_id // and for all other processes we'll use it return set_mesos_task_id(container_info, tinfo); } } } return false; } bool libsinsp::container_engine::mesos::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) { sinsp_container_info container_info; if (!match(tinfo, &container_info)) return false; tinfo->m_container_id = container_info.m_id; if (!manager->container_exists(container_info.m_id)) { container_info.m_name = container_info.m_id; manager->add_container(container_info, tinfo); manager->notify_new_container(container_info); } return true; } string libsinsp::container_engine::mesos::get_env_mesos_task_id(sinsp_threadinfo* tinfo) { string mtid; sinsp_threadinfo::visitor_func_t visitor = [&mtid] (sinsp_threadinfo *ptinfo) { // Mesos task ID detection is not a straightforward task; // this list may have to be extended. mtid = ptinfo->get_env("MESOS_TASK_ID"); // Marathon if(!mtid.empty()) { return false; } mtid = ptinfo->get_env("mesos_task_id"); // Chronos if(!mtid.empty()) { return false; } mtid = ptinfo->get_env("MESOS_EXECUTOR_ID"); // others if(!mtid.empty()) { return false; } return true; }; // Try the current thread first. visitor returns true if mtid // was not filled in. In this case we should traverse the // parents. if(tinfo && visitor(tinfo)) { tinfo->traverse_parent_state(visitor); } return mtid; } bool libsinsp::container_engine::mesos::set_mesos_task_id(sinsp_container_info* container, sinsp_threadinfo* tinfo) { ASSERT(container); ASSERT(tinfo); // there are applications that do not share their environment in /proc/[PID]/environ // since we need MESOS_TASK_ID environment variable to discover Mesos containers, // there is a workaround for such cases: // - for docker containers, we discover it directly from container, through Remote API // (see sinsp_container_manager::parse_docker() for details) // - for mesos native containers, parent process has the MESOS_TASK_ID (or equivalent, see // get_env_mesos_task_id(sinsp_threadinfo*) implementation) environment variable, so we // peek into the parent process environment to discover it if(container && tinfo) { string& mtid = container->m_mesos_task_id; if(mtid.empty()) { mtid = get_env_mesos_task_id(tinfo); // Ensure that the mesos task id vaguely looks // like a real id. We assume it must be at // least 3 characters and contain a dot or underscore if(!mtid.empty() && mtid.length()>=3 && (mtid.find_first_of("._") != std::string::npos)) { g_logger.log("Mesos native container: [" + container->m_id + "], Mesos task ID: " + mtid, sinsp_logger::SEV_DEBUG); return true; } else { g_logger.log("Mesos container [" + container->m_id + "]," "thread [" + std::to_string(tinfo->m_tid) + "], has likely malformed mesos task id [" + mtid + "], ignoring", sinsp_logger::SEV_DEBUG); } } } return false; } sysdig-0.26.4/userspace/libsinsp/container_engine/mesos.h000066400000000000000000000023231352731327100235340ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include class sinsp_container_manager; class sinsp_container_info; class sinsp_threadinfo; #include "container_engine/container_engine.h" namespace libsinsp { namespace container_engine { class mesos : public resolver { public: bool resolve(sinsp_container_manager *manager, sinsp_threadinfo *tinfo, bool query_os_for_missing_info) override; static bool set_mesos_task_id(sinsp_container_info *container, sinsp_threadinfo *tinfo); protected: bool match(sinsp_threadinfo *tinfo, sinsp_container_info *container_info); static std::string get_env_mesos_task_id(sinsp_threadinfo *tinfo); }; } } sysdig-0.26.4/userspace/libsinsp/container_engine/rkt.cpp000066400000000000000000000210751352731327100235460ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "container_engine/rkt.h" #include #include "sinsp.h" #include "sinsp_int.h" using namespace libsinsp::container_engine; bool rkt::match(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, sinsp_container_info* container_info, string& rkt_podid, string& rkt_appname, bool query_os_for_missing_info) { for(auto it = tinfo->m_cgroups.begin(); it != tinfo->m_cgroups.end(); ++it) { string cgroup = it->second; static const string COREOS_PODID_VAR = "container_uuid="; static const string SYSTEMD_UUID_ARG = "--uuid="; static const string SERVICE_SUFFIX = ".service"; if(cgroup.rfind(SERVICE_SUFFIX) == cgroup.size() - SERVICE_SUFFIX.size()) { // check if there is a parent with pod uuid var sinsp_threadinfo::visitor_func_t visitor = [&](sinsp_threadinfo* ptinfo) { for(const auto& env_var : ptinfo->get_env()) { auto container_uuid_pos = env_var.find(COREOS_PODID_VAR); if(container_uuid_pos == 0) { rkt_podid = env_var.substr(COREOS_PODID_VAR.size()); return false; } } for(const auto& arg : ptinfo->m_args) { if(arg.find(SYSTEMD_UUID_ARG) != string::npos) { rkt_podid = arg.substr(SYSTEMD_UUID_ARG.size()); return false; } } return true; }; tinfo->traverse_parent_state(visitor); if(!rkt_podid.empty()) { auto last_slash = cgroup.find_last_of("/"); rkt_appname = cgroup.substr(last_slash + 1, cgroup.size() - last_slash - SERVICE_SUFFIX.size() - 1); char image_manifest_path[SCAP_MAX_PATH_SIZE]; snprintf(image_manifest_path, sizeof(image_manifest_path), "%s/var/lib/rkt/pods/run/%s/appsinfo/%s/manifest", scap_get_host_root(), rkt_podid.c_str(), rkt_appname.c_str()); // First lookup if the container exists in our table, otherwise only if we are live check if it has // an entry in /var/lib/rkt. In capture mode only the former will be used. // In live mode former will be used only if we already hit that container bool is_rkt_pod_id_valid = manager->container_exists(rkt_podid + ":" + rkt_appname); // if it's already on our table #ifdef HAS_CAPTURE if(!is_rkt_pod_id_valid && query_os_for_missing_info) { is_rkt_pod_id_valid = (access(image_manifest_path, F_OK) == 0); } #endif if(is_rkt_pod_id_valid) { container_info->m_type = CT_RKT; container_info->m_id = rkt_podid + ":" + rkt_appname; container_info->m_name = rkt_appname; return true; } } } } // Try parsing from process root, // Strings used to detect rkt stage1-cores pods // TODO: detecting stage1-coreos rkt pods in this way is deprecated // we can remove it in the future static const string COREOS_PREFIX = "/opt/stage2/"; static const string COREOS_APP_SUFFIX = "/rootfs"; static const string COREOS_PODID_VAR = "container_uuid="; auto prefix = tinfo->m_root.find(COREOS_PREFIX); if(prefix == 0) { auto suffix = tinfo->m_root.find(COREOS_APP_SUFFIX, prefix); if(suffix != string::npos) { bool valid_id = false; rkt_appname = tinfo->m_root.substr(prefix + COREOS_PREFIX.size(), suffix - prefix - COREOS_PREFIX.size()); // It is a rkt pod with stage1-coreos sinsp_threadinfo::visitor_func_t visitor = [&] (sinsp_threadinfo *ptinfo) { for(const auto& env_var : ptinfo->get_env()) { auto container_uuid_pos = env_var.find(COREOS_PODID_VAR); if(container_uuid_pos == 0) { rkt_podid = env_var.substr(COREOS_PODID_VAR.size()); container_info->m_type = CT_RKT; container_info->m_id = rkt_podid + ":" + rkt_appname; container_info->m_name = rkt_appname; valid_id = true; return false; } } return true; }; // Try the current thread first. visitor returns true if no coreos pid // info was found. In this case we traverse the parents. if (visitor(tinfo)) { tinfo->traverse_parent_state(visitor); } return valid_id; } } else { // String used to detect stage1-fly pods static const string FLY_PREFIX = "/var/lib/rkt/pods/run/"; static const string FLY_PODID_SUFFIX = "/stage1/rootfs/opt/stage2/"; static const string FLY_APP_SUFFIX = "/rootfs"; auto prefix = tinfo->m_root.find(FLY_PREFIX); if(prefix == 0) { auto podid_suffix = tinfo->m_root.find(FLY_PODID_SUFFIX, prefix+FLY_PREFIX.size()); if(podid_suffix != string::npos) { rkt_podid = tinfo->m_root.substr(prefix + FLY_PREFIX.size(), podid_suffix - prefix - FLY_PREFIX.size()); auto appname_suffix = tinfo->m_root.find(FLY_APP_SUFFIX, podid_suffix+FLY_PODID_SUFFIX.size()); if(appname_suffix != string::npos) { rkt_appname = tinfo->m_root.substr(podid_suffix + FLY_PODID_SUFFIX.size(), appname_suffix-podid_suffix-FLY_PODID_SUFFIX.size()); container_info->m_type = CT_RKT; container_info->m_id = rkt_podid + ":" + rkt_appname; container_info->m_name = rkt_appname; return true; } } } } return false; } bool rkt::rkt::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) { sinsp_container_info container_info; string rkt_podid, rkt_appname; if (!match(manager, tinfo, &container_info, rkt_podid, rkt_appname, query_os_for_missing_info)) { return false; } tinfo->m_container_id = container_info.m_id; if (!query_os_for_missing_info || manager->container_exists(container_info.m_id)) { return true; } #ifndef _WIN32 bool have_rkt = parse_rkt(&container_info, rkt_podid, rkt_appname); #else bool have_rkt = true; #endif if (have_rkt) { manager->add_container(container_info, tinfo); manager->notify_new_container(container_info); return true; } else { return false; } } bool rkt::rkt::parse_rkt(sinsp_container_info *container, const string &podid, const string &appname) { bool ret = false; Json::Reader reader; Json::Value jroot; char image_manifest_path[SCAP_MAX_PATH_SIZE]; snprintf(image_manifest_path, sizeof(image_manifest_path), "%s/var/lib/rkt/pods/run/%s/appsinfo/%s/manifest", scap_get_host_root(), podid.c_str(), appname.c_str()); ifstream image_manifest(image_manifest_path); if(reader.parse(image_manifest, jroot)) { container->m_image = jroot["name"].asString(); for(const auto& label_entry : jroot["labels"]) { container->m_labels.emplace(label_entry["name"].asString(), label_entry["value"].asString()); } auto version_label_it = container->m_labels.find("version"); if(version_label_it != container->m_labels.end()) { container->m_image += ":" + version_label_it->second; } ret = true; } char net_info_path[SCAP_MAX_PATH_SIZE]; snprintf(net_info_path, sizeof(net_info_path), "%s/var/lib/rkt/pods/run/%s/net-info.json", scap_get_host_root(), podid.c_str()); ifstream net_info(net_info_path); if(reader.parse(net_info, jroot) && jroot.size() > 0) { const auto& first_net = jroot[0]; if(inet_pton(AF_INET, first_net["ip"].asCString(), &container->m_container_ip) == -1) { ASSERT(false); } container->m_container_ip = ntohl(container->m_container_ip); } char pod_manifest_path[SCAP_MAX_PATH_SIZE]; snprintf(pod_manifest_path, sizeof(pod_manifest_path), "%s/var/lib/rkt/pods/run/%s/pod", scap_get_host_root(), podid.c_str()); ifstream pod_manifest(pod_manifest_path); unordered_map image_ports; if(reader.parse(pod_manifest, jroot) && jroot.size() > 0) { for(const auto& japp : jroot["apps"]) { if (japp["name"].asString() == appname) { for(const auto& image_port : japp["app"]["ports"]) { image_ports[image_port["name"].asString()] = image_port["port"].asUInt(); } break; } } for(const auto& jport : jroot["ports"]) { auto host_port = jport["hostPort"].asUInt(); auto container_port_it = image_ports.find(jport["name"].asString()); if(host_port > 0 && container_port_it != image_ports.end()) { sinsp_container_info::container_port_mapping port_mapping; port_mapping.m_host_port = host_port; port_mapping.m_container_port = container_port_it->second; container->m_port_mappings.emplace_back(move(port_mapping)); } } } return ret; } sysdig-0.26.4/userspace/libsinsp/container_engine/rkt.h000066400000000000000000000024231352731327100232070ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include class sinsp_container_manager; class sinsp_container_info; class sinsp_threadinfo; #include "container_engine/container_engine.h" namespace libsinsp { namespace container_engine { class rkt : public resolver { public: bool resolve(sinsp_container_manager *manager, sinsp_threadinfo *tinfo, bool query_os_for_missing_info) override; protected: bool match(sinsp_container_manager *manager, sinsp_threadinfo *tinfo, sinsp_container_info *container_info, std::string &rkt_podid, std::string &rkt_appname, bool query_os_for_missing_info); bool parse_rkt(sinsp_container_info *container, const std::string &podid, const std::string &appname); }; } } sysdig-0.26.4/userspace/libsinsp/container_info.cpp000066400000000000000000000115341352731327100224330ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "container_info.h" #include "sinsp.h" #include "sinsp_int.h" std::vector sinsp_container_info::container_health_probe::probe_type_names = { "None", "Healthcheck", "LivenessProbe", "ReadinessProbe", "End" }; sinsp_container_info::container_health_probe::container_health_probe() { } sinsp_container_info::container_health_probe::container_health_probe(const probe_type ptype, const std::string &&exe, const std::vector &&args) : m_probe_type(ptype), m_health_probe_exe(exe), m_health_probe_args(args) { } sinsp_container_info::container_health_probe::~container_health_probe() { } void sinsp_container_info::container_health_probe::parse_health_probes(const Json::Value &config_obj, std::list &probes) { // Add any health checks described in the container config/labels. for(int i=PT_NONE; i != PT_END; i++) { string key = probe_type_names[i]; const Json::Value& probe_obj = config_obj[key]; if(!probe_obj.isNull() && probe_obj.isObject()) { const Json::Value& probe_exe_obj = probe_obj["exe"]; if(!probe_exe_obj.isNull() && probe_exe_obj.isConvertibleTo(Json::stringValue)) { const Json::Value& probe_args_obj = probe_obj["args"]; std::string probe_exe = probe_exe_obj.asString(); std::vector probe_args; if(!probe_args_obj.isNull() && probe_args_obj.isArray()) { for(const auto &item : probe_args_obj) { if(item.isConvertibleTo(Json::stringValue)) { probe_args.push_back(item.asString()); } } } g_logger.format(sinsp_logger::SEV_DEBUG, "add_health_probes: adding %s %s %d", probe_type_names[i].c_str(), probe_exe.c_str(), probe_args.size()); probes.emplace_back(static_cast(i), std::move(probe_exe), std::move(probe_args)); } } } } void sinsp_container_info::container_health_probe::add_health_probes(const std::list &probes, Json::Value &config_obj) { for(auto &probe : probes) { string key = probe_type_names[probe.m_probe_type]; Json::Value args; config_obj[key]["exe"] = probe.m_health_probe_exe; for(auto &arg : probe.m_health_probe_args) { args.append(arg); } config_obj[key]["args"] = args; } } const sinsp_container_info::container_mount_info *sinsp_container_info::mount_by_idx(uint32_t idx) const { if (idx >= m_mounts.size()) { return NULL; } return &(m_mounts[idx]); } const sinsp_container_info::container_mount_info *sinsp_container_info::mount_by_source(std::string &source) const { // note: linear search for (auto &mntinfo :m_mounts) { if(sinsp_utils::glob_match(source.c_str(), mntinfo.m_source.c_str())) { return &mntinfo; } } return NULL; } const sinsp_container_info::container_mount_info *sinsp_container_info::mount_by_dest(std::string &dest) const { // note: linear search for (auto &mntinfo :m_mounts) { if(sinsp_utils::glob_match(dest.c_str(), mntinfo.m_dest.c_str())) { return &mntinfo; } } return NULL; } std::shared_ptr sinsp_container_info::get_tinfo(sinsp* inspector) const { auto tinfo = make_shared(inspector); tinfo->m_tid = -1; tinfo->m_pid = -1; tinfo->m_vtid = -2; tinfo->m_vpid = -2; tinfo->m_comm = "container:" + m_id; tinfo->m_container_id = m_id; return tinfo; } sinsp_container_info::container_health_probe::probe_type sinsp_container_info::match_health_probe(sinsp_threadinfo *tinfo) { g_logger.format(sinsp_logger::SEV_DEBUG, "match_health_probe (%s): %u health probes to consider", m_id.c_str(), m_health_probes.size()); auto pred = [&] (container_health_probe &p) { g_logger.format(sinsp_logger::SEV_DEBUG, "match_health_probe (%s): Matching tinfo %s %d against %s %d", m_id.c_str(), tinfo->m_exe.c_str(), tinfo->m_args.size(), p.m_health_probe_exe.c_str(), p.m_health_probe_args.size()); return (p.m_health_probe_exe == tinfo->m_exe && p.m_health_probe_args == tinfo->m_args); }; auto match = std::find_if(m_health_probes.begin(), m_health_probes.end(), pred); if(match == m_health_probes.end()) { return container_health_probe::PT_NONE; } return match->m_probe_type; } sysdig-0.26.4/userspace/libsinsp/container_info.h000066400000000000000000000132371352731327100221020ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #include #include #include #include #include #include "json/json.h" class sinsp; class sinsp_threadinfo; enum sinsp_container_type { CT_DOCKER = 0, CT_LXC = 1, CT_LIBVIRT_LXC = 2, CT_MESOS = 3, CT_RKT = 4, CT_CUSTOM = 5, CT_CRI = 6, CT_CONTAINERD = 7, CT_CRIO = 8, CT_BPM = 9, }; class sinsp_threadinfo; // Docker and CRI-compatible runtimes are very similar static inline bool is_docker_compatible(sinsp_container_type t) { return t == CT_DOCKER || t == CT_CRI || t == CT_CONTAINERD || t == CT_CRIO; } class sinsp_container_info { public: class container_port_mapping { public: container_port_mapping(): m_host_ip(0), m_host_port(0), m_container_port(0) { } uint32_t m_host_ip; uint16_t m_host_port; uint16_t m_container_port; }; class container_mount_info { public: container_mount_info(): m_source(""), m_dest(""), m_mode(""), m_rdwr(false), m_propagation("") { } container_mount_info(const std::string&& source, const std::string&& dest, const std::string&& mode, const bool rw, const std::string&& propagation) : m_source(source), m_dest(dest), m_mode(mode), m_rdwr(rw), m_propagation(propagation) { } container_mount_info(const Json::Value &source, const Json::Value &dest, const Json::Value &mode, const Json::Value &rw, const Json::Value &propagation) { get_string_value(source, m_source); get_string_value(dest, m_dest); get_string_value(mode, m_mode); get_string_value(propagation, m_propagation); if(!rw.isNull() && rw.isBool()) { m_rdwr = rw.asBool(); } } std::string to_string() const { return m_source + ":" + m_dest + ":" + m_mode + ":" + (m_rdwr ? "true" : "false") + ":" + m_propagation; } inline void get_string_value(const Json::Value &val, std::string &result) { if(!val.isNull() && val.isString()) { result = val.asString(); } } std::string m_source; std::string m_dest; std::string m_mode; bool m_rdwr; std::string m_propagation; }; class container_health_probe { public: // The type of health probe enum probe_type { PT_NONE = 0, PT_HEALTHCHECK, PT_LIVENESS_PROBE, PT_READINESS_PROBE, PT_END }; // String representations of the above, suitable for // parsing to/from json. Should be kept in sync with // probe_type enum. static std::vector probe_type_names; // Parse any health probes out of the provided // container json, updating the list of probes. static void parse_health_probes(const Json::Value &config_obj, std::list &probes); // Serialize the list of health probes, adding to the provided json object static void add_health_probes(const std::list &probes, Json::Value &config_obj); container_health_probe(); container_health_probe(const probe_type probe_type, const std::string &&exe, const std::vector &&args); virtual ~container_health_probe(); // The probe_type that should be used for commands // matching this health probe. probe_type m_probe_type; // The actual health probe exe and args. std::string m_health_probe_exe; std::vector m_health_probe_args; }; sinsp_container_info(): m_container_ip(0), m_privileged(false), m_memory_limit(0), m_swap_limit(0), m_cpu_shares(1024), m_cpu_quota(0), m_cpu_period(100000), m_cpuset_cpu_count(0), m_is_pod_sandbox(false), m_metadata_complete(true), m_metadata_deadline(0) { } const std::vector& get_env() const { return m_env; } const container_mount_info *mount_by_idx(uint32_t idx) const; const container_mount_info *mount_by_source(std::string &source) const; const container_mount_info *mount_by_dest(std::string &dest) const; bool is_pod_sandbox() const { return m_is_pod_sandbox; } std::shared_ptr get_tinfo(sinsp* inspector) const; // Match a process against the set of health probes container_health_probe::probe_type match_health_probe(sinsp_threadinfo *tinfo); std::string m_id; sinsp_container_type m_type; std::string m_name; std::string m_image; std::string m_imageid; std::string m_imagerepo; std::string m_imagetag; std::string m_imagedigest; uint32_t m_container_ip; bool m_privileged; std::vector m_mounts; std::vector m_port_mappings; std::map m_labels; std::vector m_env; std::string m_mesos_task_id; int64_t m_memory_limit; int64_t m_swap_limit; int64_t m_cpu_shares; int64_t m_cpu_quota; int64_t m_cpu_period; int32_t m_cpuset_cpu_count; std::list m_health_probes; bool m_is_pod_sandbox; // If false, this represents incomplete information about the // container that will be filled in later as a result of an // async fetch of container info. bool m_metadata_complete; #ifdef HAS_ANALYZER std::string m_sysdig_agent_conf; #endif uint64_t m_metadata_deadline; }; sysdig-0.26.4/userspace/libsinsp/cri.cpp000066400000000000000000000213621352731327100202130ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "cri.h" #include #ifdef GRPC_INCLUDE_IS_GRPCPP # include #else # include #endif #include "sinsp.h" #include "sinsp_int.h" namespace { bool pod_uses_host_netns(const runtime::v1alpha2::PodSandboxStatusResponse& resp) { const auto netns = resp.status().linux().namespaces().options().network(); return netns == runtime::v1alpha2::NODE; } } namespace libsinsp { namespace cri { std::string s_cri_unix_socket_path = "/run/containerd/containerd.sock"; std::unique_ptr s_cri = nullptr; std::unique_ptr s_cri_image = nullptr; int64_t s_cri_timeout = 1000; sinsp_container_type s_cri_runtime_type = CT_CRI; bool s_cri_extra_queries = true; sinsp_container_type get_cri_runtime_type(const std::string &runtime_name) { if(runtime_name == "containerd") { return CT_CONTAINERD; } else if(runtime_name == "cri-o") { return CT_CRIO; } else { return CT_CRI; } } bool parse_cri_image(const runtime::v1alpha2::ContainerStatus &status, sinsp_container_info *container) { // image_ref may be one of two forms: // host/image@sha256:digest // sha256:digest bool have_digest = false; const auto &image_ref = status.image_ref(); auto digest_start = image_ref.find("sha256:"); switch (digest_start) { case 0: // sha256:digest have_digest = true; break; case string::npos: break; default: // host/image@sha256:digest have_digest = image_ref[digest_start - 1] == '@'; } string hostname, port, digest; sinsp_utils::split_container_image(status.image().image(), hostname, port, container->m_imagerepo, container->m_imagetag, digest, false); container->m_image = status.image().image(); if(have_digest) { container->m_imagedigest = image_ref.substr(digest_start); } else { container->m_imagedigest = digest; } return true; } bool parse_cri_mounts(const runtime::v1alpha2::ContainerStatus &status, sinsp_container_info *container) { for(const auto &mount : status.mounts()) { const char *propagation; switch(mount.propagation()) { case runtime::v1alpha2::MountPropagation::PROPAGATION_PRIVATE: propagation = "private"; break; case runtime::v1alpha2::MountPropagation::PROPAGATION_HOST_TO_CONTAINER: propagation = "rslave"; break; case runtime::v1alpha2::MountPropagation::PROPAGATION_BIDIRECTIONAL: propagation = "rshared"; break; default: propagation = "unknown"; break; } container->m_mounts.emplace_back( mount.host_path(), mount.container_path(), "", !mount.readonly(), propagation); } return true; } bool walk_down_json(const Json::Value &root, const Json::Value **out, const std::string &key) { if(root.isMember(key)) { *out = &root[key]; return true; } return false; } template bool walk_down_json(const Json::Value &root, const Json::Value **out, const std::string &key, Args... args) { if(root.isMember(key)) { return walk_down_json(root[key], out, args...); } return false; } bool set_numeric(const Json::Value &dict, const std::string &key, int64_t &val) { if(!dict.isMember(key)) { return false; } const auto &json_val = dict[key]; if(!json_val.isNumeric()) { return false; } val = json_val.asInt64(); return true; } bool parse_cri_env(const Json::Value &info, sinsp_container_info *container) { const Json::Value *envs; if(!walk_down_json(info, &envs, "config", "envs") || !envs->isArray()) { return false; } for(const auto &env_var : *envs) { const auto &key = env_var["key"]; const auto &value = env_var["value"]; if(key.isString() && value.isString()) { auto var = key.asString(); var += '='; var += value.asString(); container->m_env.emplace_back(var); } } return true; } bool parse_cri_json_image(const Json::Value &info, sinsp_container_info *container) { const Json::Value *image; if(!walk_down_json(info, &image, "config", "image", "image") || !image->isString()) { return false; } auto image_str = image->asString(); auto pos = image_str.find(':'); if(pos == string::npos) { container->m_imageid = move(image_str); } else { container->m_imageid = image_str.substr(pos + 1); } return true; } bool parse_cri_runtime_spec(const Json::Value &info, sinsp_container_info *container) { const Json::Value *linux = nullptr; if(!walk_down_json(info, &linux, "runtimeSpec", "linux") || !linux->isObject()) { return false; } const Json::Value *memory = nullptr; if(walk_down_json(*linux, &memory, "resources", "memory")) { set_numeric(*memory, "limit", container->m_memory_limit); container->m_swap_limit = container->m_memory_limit; } const Json::Value *cpu = nullptr; if(walk_down_json(*linux, &cpu, "resources", "cpu") && cpu->isObject()) { set_numeric(*cpu, "shares", container->m_cpu_shares); set_numeric(*cpu, "quota", container->m_cpu_quota); set_numeric(*cpu, "period", container->m_cpu_period); } const Json::Value *privileged; if(walk_down_json(*linux, &privileged, "security_context", "privileged") && privileged->isBool()) { container->m_privileged = privileged->asBool(); } return true; } bool is_pod_sandbox(const std::string &container_id) { runtime::v1alpha2::PodSandboxStatusRequest req; runtime::v1alpha2::PodSandboxStatusResponse resp; req.set_pod_sandbox_id(container_id); req.set_verbose(true); grpc::ClientContext context; auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(s_cri_timeout); context.set_deadline(deadline); grpc::Status status = s_cri->PodSandboxStatus(&context, req, &resp); return status.ok(); } uint32_t get_pod_sandbox_ip(const std::string &pod_sandbox_id) { runtime::v1alpha2::PodSandboxStatusRequest req; runtime::v1alpha2::PodSandboxStatusResponse resp; req.set_pod_sandbox_id(pod_sandbox_id); req.set_verbose(true); grpc::ClientContext context; auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(s_cri_timeout); context.set_deadline(deadline); grpc::Status status = s_cri->PodSandboxStatus(&context, req, &resp); if(!status.ok()) { return 0; } if(pod_uses_host_netns(resp)) { return 0; } const auto &pod_ip = resp.status().network().ip(); if(pod_ip.empty()) { return 0; } uint32_t ip; if(inet_pton(AF_INET, pod_ip.c_str(), &ip) == -1) { ASSERT(false); return 0; } else { return ip; } } uint32_t get_container_ip(const std::string &container_id) { runtime::v1alpha2::ListContainersRequest req; runtime::v1alpha2::ListContainersResponse resp; auto filter = req.mutable_filter(); filter->set_id(container_id); grpc::ClientContext context; auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(s_cri_timeout); context.set_deadline(deadline); grpc::Status lstatus = s_cri->ListContainers(&context, req, &resp); switch(resp.containers_size()) { case 0: g_logger.format(sinsp_logger::SEV_WARNING, "Container id %s not in list from CRI", container_id.c_str()); ASSERT(false); break; case 1: { const auto& cri_container = resp.containers(0); return ntohl(get_pod_sandbox_ip(cri_container.pod_sandbox_id())); } default: g_logger.format(sinsp_logger::SEV_WARNING, "Container id %s matches more than once in list from CRI", container_id.c_str()); ASSERT(false); break; } return 0; } std::string get_container_image_id(const std::string &image_ref) { runtime::v1alpha2::ListImagesRequest req; runtime::v1alpha2::ListImagesResponse resp; auto filter = req.mutable_filter(); auto spec = filter->mutable_image(); spec->set_image(image_ref); grpc::ClientContext context; auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(s_cri_timeout); context.set_deadline(deadline); grpc::Status status = s_cri_image->ListImages(&context, req, &resp); switch(resp.images_size()) { case 0: g_logger.format(sinsp_logger::SEV_WARNING, "Image ref %s not in list from CRI", image_ref.c_str()); ASSERT(false); break; case 1: { const auto& image = resp.images(0); return image.id(); } default: g_logger.format(sinsp_logger::SEV_WARNING, "Image ref %s matches more than once in list from CRI", image_ref.c_str()); ASSERT(false); break; } return ""; } } } sysdig-0.26.4/userspace/libsinsp/cri.h000066400000000000000000000035141352731327100176570ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #include #include "cri.pb.h" #include "cri.grpc.pb.h" #include "container_info.h" namespace libsinsp { namespace cri { extern std::string s_cri_unix_socket_path; extern std::unique_ptr s_cri; extern std::unique_ptr s_cri_image; extern int64_t s_cri_timeout; extern sinsp_container_type s_cri_runtime_type; extern bool s_cri_extra_queries; sinsp_container_type get_cri_runtime_type(const std::string &runtime_name); bool parse_cri_image(const runtime::v1alpha2::ContainerStatus &status, sinsp_container_info *container); bool parse_cri_mounts(const runtime::v1alpha2::ContainerStatus &status, sinsp_container_info *container); bool parse_cri_env(const Json::Value &info, sinsp_container_info *container); bool parse_cri_json_image(const Json::Value &info, sinsp_container_info *container); bool parse_cri_runtime_spec(const Json::Value &info, sinsp_container_info *container); bool is_pod_sandbox(const std::string &container_id); uint32_t get_pod_sandbox_ip(const std::string &pod_sandbox_id); uint32_t get_container_ip(const std::string &container_id); std::string get_container_image_id(const std::string &image_ref); } } sysdig-0.26.4/userspace/libsinsp/cri.proto000066400000000000000000001405651352731327100206030ustar00rootroot00000000000000/* Copyright 2018 The Kubernetes 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. */ // To regenerate api.pb.go run hack/update-generated-runtime.sh syntax = 'proto3'; package runtime.v1alpha2; option go_package = "v1alpha2"; // Runtime service defines the public APIs for remote container runtimes service RuntimeService { // Version returns the runtime name, runtime version, and runtime API version. rpc Version(VersionRequest) returns (VersionResponse) {} // RunPodSandbox creates and starts a pod-level sandbox. Runtimes must ensure // the sandbox is in the ready state on success. rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {} // StopPodSandbox stops any running process that is part of the sandbox and // reclaims network resources (e.g., IP addresses) allocated to the sandbox. // If there are any running containers in the sandbox, they must be forcibly // terminated. // This call is idempotent, and must not return an error if all relevant // resources have already been reclaimed. kubelet will call StopPodSandbox // at least once before calling RemovePodSandbox. It will also attempt to // reclaim resources eagerly, as soon as a sandbox is not needed. Hence, // multiple StopPodSandbox calls are expected. rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse) {} // RemovePodSandbox removes the sandbox. If there are any running containers // in the sandbox, they must be forcibly terminated and removed. // This call is idempotent, and must not return an error if the sandbox has // already been removed. rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse) {} // PodSandboxStatus returns the status of the PodSandbox. If the PodSandbox is not // present, returns an error. rpc PodSandboxStatus(PodSandboxStatusRequest) returns (PodSandboxStatusResponse) {} // ListPodSandbox returns a list of PodSandboxes. rpc ListPodSandbox(ListPodSandboxRequest) returns (ListPodSandboxResponse) {} // CreateContainer creates a new container in specified PodSandbox rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {} // StartContainer starts the container. rpc StartContainer(StartContainerRequest) returns (StartContainerResponse) {} // StopContainer stops a running container with a grace period (i.e., timeout). // This call is idempotent, and must not return an error if the container has // already been stopped. // TODO: what must the runtime do after the grace period is reached? rpc StopContainer(StopContainerRequest) returns (StopContainerResponse) {} // RemoveContainer removes the container. If the container is running, the // container must be forcibly removed. // This call is idempotent, and must not return an error if the container has // already been removed. rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse) {} // ListContainers lists all containers by filters. rpc ListContainers(ListContainersRequest) returns (ListContainersResponse) {} // ContainerStatus returns status of the container. If the container is not // present, returns an error. rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) {} // UpdateContainerResources updates ContainerConfig of the container. rpc UpdateContainerResources(UpdateContainerResourcesRequest) returns (UpdateContainerResourcesResponse) {} // ReopenContainerLog asks runtime to reopen the stdout/stderr log file // for the container. This is often called after the log file has been // rotated. If the container is not running, container runtime can choose // to either create a new log file and return nil, or return an error. // Once it returns error, new container log file MUST NOT be created. rpc ReopenContainerLog(ReopenContainerLogRequest) returns (ReopenContainerLogResponse) {} // ExecSync runs a command in a container synchronously. rpc ExecSync(ExecSyncRequest) returns (ExecSyncResponse) {} // Exec prepares a streaming endpoint to execute a command in the container. rpc Exec(ExecRequest) returns (ExecResponse) {} // Attach prepares a streaming endpoint to attach to a running container. rpc Attach(AttachRequest) returns (AttachResponse) {} // PortForward prepares a streaming endpoint to forward ports from a PodSandbox. rpc PortForward(PortForwardRequest) returns (PortForwardResponse) {} // ContainerStats returns stats of the container. If the container does not // exist, the call returns an error. rpc ContainerStats(ContainerStatsRequest) returns (ContainerStatsResponse) {} // ListContainerStats returns stats of all running containers. rpc ListContainerStats(ListContainerStatsRequest) returns (ListContainerStatsResponse) {} // UpdateRuntimeConfig updates the runtime configuration based on the given request. rpc UpdateRuntimeConfig(UpdateRuntimeConfigRequest) returns (UpdateRuntimeConfigResponse) {} // Status returns the status of the runtime. rpc Status(StatusRequest) returns (StatusResponse) {} } // ImageService defines the public APIs for managing images. service ImageService { // ListImages lists existing images. rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {} // ImageStatus returns the status of the image. If the image is not // present, returns a response with ImageStatusResponse.Image set to // nil. rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) {} // PullImage pulls an image with authentication config. rpc PullImage(PullImageRequest) returns (PullImageResponse) {} // RemoveImage removes the image. // This call is idempotent, and must not return an error if the image has // already been removed. rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) {} // ImageFSInfo returns information of the filesystem that is used to store images. rpc ImageFsInfo(ImageFsInfoRequest) returns (ImageFsInfoResponse) {} } message VersionRequest { // Version of the kubelet runtime API. string version = 1; } message VersionResponse { // Version of the kubelet runtime API. string version = 1; // Name of the container runtime. string runtime_name = 2; // Version of the container runtime. The string must be // semver-compatible. string runtime_version = 3; // API version of the container runtime. The string must be // semver-compatible. string runtime_api_version = 4; } // DNSConfig specifies the DNS servers and search domains of a sandbox. message DNSConfig { // List of DNS servers of the cluster. repeated string servers = 1; // List of DNS search domains of the cluster. repeated string searches = 2; // List of DNS options. See https://linux.die.net/man/5/resolv.conf // for all available options. repeated string options = 3; } enum Protocol { TCP = 0; UDP = 1; SCTP = 2; } // PortMapping specifies the port mapping configurations of a sandbox. message PortMapping { // Protocol of the port mapping. Protocol protocol = 1; // Port number within the container. Default: 0 (not specified). int32 container_port = 2; // Port number on the host. Default: 0 (not specified). int32 host_port = 3; // Host IP. string host_ip = 4; } enum MountPropagation { // No mount propagation ("private" in Linux terminology). PROPAGATION_PRIVATE = 0; // Mounts get propagated from the host to the container ("rslave" in Linux). PROPAGATION_HOST_TO_CONTAINER = 1; // Mounts get propagated from the host to the container and from the // container to the host ("rshared" in Linux). PROPAGATION_BIDIRECTIONAL = 2; } // Mount specifies a host volume to mount into a container. message Mount { // Path of the mount within the container. string container_path = 1; // Path of the mount on the host. If the hostPath doesn't exist, then runtimes // should report error. If the hostpath is a symbolic link, runtimes should // follow the symlink and mount the real destination to container. string host_path = 2; // If set, the mount is read-only. bool readonly = 3; // If set, the mount needs SELinux relabeling. bool selinux_relabel = 4; // Requested propagation mode. MountPropagation propagation = 5; } // A NamespaceMode describes the intended namespace configuration for each // of the namespaces (Network, PID, IPC) in NamespaceOption. Runtimes should // map these modes as appropriate for the technology underlying the runtime. enum NamespaceMode { // A POD namespace is common to all containers in a pod. // For example, a container with a PID namespace of POD expects to view // all of the processes in all of the containers in the pod. POD = 0; // A CONTAINER namespace is restricted to a single container. // For example, a container with a PID namespace of CONTAINER expects to // view only the processes in that container. CONTAINER = 1; // A NODE namespace is the namespace of the Kubernetes node. // For example, a container with a PID namespace of NODE expects to view // all of the processes on the host running the kubelet. NODE = 2; } // NamespaceOption provides options for Linux namespaces. message NamespaceOption { // Network namespace for this container/sandbox. // Note: There is currently no way to set CONTAINER scoped network in the Kubernetes API. // Namespaces currently set by the kubelet: POD, NODE NamespaceMode network = 1; // PID namespace for this container/sandbox. // Note: The CRI default is POD, but the v1.PodSpec default is CONTAINER. // The kubelet's runtime manager will set this to CONTAINER explicitly for v1 pods. // Namespaces currently set by the kubelet: POD, CONTAINER, NODE NamespaceMode pid = 2; // IPC namespace for this container/sandbox. // Note: There is currently no way to set CONTAINER scoped IPC in the Kubernetes API. // Namespaces currently set by the kubelet: POD, NODE NamespaceMode ipc = 3; } // Int64Value is the wrapper of int64. message Int64Value { // The value. int64 value = 1; } // LinuxSandboxSecurityContext holds linux security configuration that will be // applied to a sandbox. Note that: // 1) It does not apply to containers in the pods. // 2) It may not be applicable to a PodSandbox which does not contain any running // process. message LinuxSandboxSecurityContext { // Configurations for the sandbox's namespaces. // This will be used only if the PodSandbox uses namespace for isolation. NamespaceOption namespace_options = 1; // Optional SELinux context to be applied. SELinuxOption selinux_options = 2; // UID to run sandbox processes as, when applicable. Int64Value run_as_user = 3; // GID to run sandbox processes as, when applicable. run_as_group should only // be specified when run_as_user is specified; otherwise, the runtime MUST error. Int64Value run_as_group = 8; // If set, the root filesystem of the sandbox is read-only. bool readonly_rootfs = 4; // List of groups applied to the first process run in the sandbox, in // addition to the sandbox's primary GID. repeated int64 supplemental_groups = 5; // Indicates whether the sandbox will be asked to run a privileged // container. If a privileged container is to be executed within it, this // MUST be true. // This allows a sandbox to take additional security precautions if no // privileged containers are expected to be run. bool privileged = 6; // Seccomp profile for the sandbox, candidate values are: // * runtime/default: the default profile for the container runtime // * unconfined: unconfined profile, ie, no seccomp sandboxing // * localhost/: the profile installed on the node. // is the full path of the profile. // Default: "", which is identical with unconfined. string seccomp_profile_path = 7; } // LinuxPodSandboxConfig holds platform-specific configurations for Linux // host platforms and Linux-based containers. message LinuxPodSandboxConfig { // Parent cgroup of the PodSandbox. // The cgroupfs style syntax will be used, but the container runtime can // convert it to systemd semantics if needed. string cgroup_parent = 1; // LinuxSandboxSecurityContext holds sandbox security attributes. LinuxSandboxSecurityContext security_context = 2; // Sysctls holds linux sysctls config for the sandbox. map sysctls = 3; } // PodSandboxMetadata holds all necessary information for building the sandbox name. // The container runtime is encouraged to expose the metadata associated with the // PodSandbox in its user interface for better user experience. For example, // the runtime can construct a unique PodSandboxName based on the metadata. message PodSandboxMetadata { // Pod name of the sandbox. Same as the pod name in the PodSpec. string name = 1; // Pod UID of the sandbox. Same as the pod UID in the PodSpec. string uid = 2; // Pod namespace of the sandbox. Same as the pod namespace in the PodSpec. string namespace = 3; // Attempt number of creating the sandbox. Default: 0. uint32 attempt = 4; } // PodSandboxConfig holds all the required and optional fields for creating a // sandbox. message PodSandboxConfig { // Metadata of the sandbox. This information will uniquely identify the // sandbox, and the runtime should leverage this to ensure correct // operation. The runtime may also use this information to improve UX, such // as by constructing a readable name. PodSandboxMetadata metadata = 1; // Hostname of the sandbox. string hostname = 2; // Path to the directory on the host in which container log files are // stored. // By default the log of a container going into the LogDirectory will be // hooked up to STDOUT and STDERR. However, the LogDirectory may contain // binary log files with structured logging data from the individual // containers. For example, the files might be newline separated JSON // structured logs, systemd-journald journal files, gRPC trace files, etc. // E.g., // PodSandboxConfig.LogDirectory = `/var/log/pods//` // ContainerConfig.LogPath = `containerName/Instance#.log` // // WARNING: Log management and how kubelet should interface with the // container logs are under active discussion in // https://issues.k8s.io/24677. There *may* be future change of direction // for logging as the discussion carries on. string log_directory = 3; // DNS config for the sandbox. DNSConfig dns_config = 4; // Port mappings for the sandbox. repeated PortMapping port_mappings = 5; // Key-value pairs that may be used to scope and select individual resources. map labels = 6; // Unstructured key-value map that may be set by the kubelet to store and // retrieve arbitrary metadata. This will include any annotations set on a // pod through the Kubernetes API. // // Annotations MUST NOT be altered by the runtime; the annotations stored // here MUST be returned in the PodSandboxStatus associated with the pod // this PodSandboxConfig creates. // // In general, in order to preserve a well-defined interface between the // kubelet and the container runtime, annotations SHOULD NOT influence // runtime behaviour. // // Annotations can also be useful for runtime authors to experiment with // new features that are opaque to the Kubernetes APIs (both user-facing // and the CRI). Whenever possible, however, runtime authors SHOULD // consider proposing new typed fields for any new features instead. map annotations = 7; // Optional configurations specific to Linux hosts. LinuxPodSandboxConfig linux = 8; } message RunPodSandboxRequest { // Configuration for creating a PodSandbox. PodSandboxConfig config = 1; // Named runtime configuration to use for this PodSandbox. // If the runtime handler is unknown, this request should be rejected. An // empty string should select the default handler, equivalent to the // behavior before this feature was added. // See https://git.k8s.io/community/keps/sig-node/0014-runtime-class.md string runtime_handler = 2; } message RunPodSandboxResponse { // ID of the PodSandbox to run. string pod_sandbox_id = 1; } message StopPodSandboxRequest { // ID of the PodSandbox to stop. string pod_sandbox_id = 1; } message StopPodSandboxResponse {} message RemovePodSandboxRequest { // ID of the PodSandbox to remove. string pod_sandbox_id = 1; } message RemovePodSandboxResponse {} message PodSandboxStatusRequest { // ID of the PodSandbox for which to retrieve status. string pod_sandbox_id = 1; // Verbose indicates whether to return extra information about the pod sandbox. bool verbose = 2; } // PodSandboxNetworkStatus is the status of the network for a PodSandbox. message PodSandboxNetworkStatus { // IP address of the PodSandbox. string ip = 1; } // Namespace contains paths to the namespaces. message Namespace { // Namespace options for Linux namespaces. NamespaceOption options = 2; } // LinuxSandboxStatus contains status specific to Linux sandboxes. message LinuxPodSandboxStatus { // Paths to the sandbox's namespaces. Namespace namespaces = 1; } enum PodSandboxState { SANDBOX_READY = 0; SANDBOX_NOTREADY = 1; } // PodSandboxStatus contains the status of the PodSandbox. message PodSandboxStatus { // ID of the sandbox. string id = 1; // Metadata of the sandbox. PodSandboxMetadata metadata = 2; // State of the sandbox. PodSandboxState state = 3; // Creation timestamp of the sandbox in nanoseconds. Must be > 0. int64 created_at = 4; // Network contains network status if network is handled by the runtime. PodSandboxNetworkStatus network = 5; // Linux-specific status to a pod sandbox. LinuxPodSandboxStatus linux = 6; // Labels are key-value pairs that may be used to scope and select individual resources. map labels = 7; // Unstructured key-value map holding arbitrary metadata. // Annotations MUST NOT be altered by the runtime; the value of this field // MUST be identical to that of the corresponding PodSandboxConfig used to // instantiate the pod sandbox this status represents. map annotations = 8; } message PodSandboxStatusResponse { // Status of the PodSandbox. PodSandboxStatus status = 1; // Info is extra information of the PodSandbox. The key could be arbitrary string, and // value should be in json format. The information could include anything useful for // debug, e.g. network namespace for linux container based container runtime. // It should only be returned non-empty when Verbose is true. map info = 2; } // PodSandboxStateValue is the wrapper of PodSandboxState. message PodSandboxStateValue { // State of the sandbox. PodSandboxState state = 1; } // PodSandboxFilter is used to filter a list of PodSandboxes. // All those fields are combined with 'AND' message PodSandboxFilter { // ID of the sandbox. string id = 1; // State of the sandbox. PodSandboxStateValue state = 2; // LabelSelector to select matches. // Only api.MatchLabels is supported for now and the requirements // are ANDed. MatchExpressions is not supported yet. map label_selector = 3; } message ListPodSandboxRequest { // PodSandboxFilter to filter a list of PodSandboxes. PodSandboxFilter filter = 1; } // PodSandbox contains minimal information about a sandbox. message PodSandbox { // ID of the PodSandbox. string id = 1; // Metadata of the PodSandbox. PodSandboxMetadata metadata = 2; // State of the PodSandbox. PodSandboxState state = 3; // Creation timestamps of the PodSandbox in nanoseconds. Must be > 0. int64 created_at = 4; // Labels of the PodSandbox. map labels = 5; // Unstructured key-value map holding arbitrary metadata. // Annotations MUST NOT be altered by the runtime; the value of this field // MUST be identical to that of the corresponding PodSandboxConfig used to // instantiate this PodSandbox. map annotations = 6; } message ListPodSandboxResponse { // List of PodSandboxes. repeated PodSandbox items = 1; } // ImageSpec is an internal representation of an image. Currently, it wraps the // value of a Container's Image field (e.g. imageID or imageDigest), but in the // future it will include more detailed information about the different image types. message ImageSpec { string image = 1; } message KeyValue { string key = 1; string value = 2; } // LinuxContainerResources specifies Linux specific configuration for // resources. // TODO: Consider using Resources from opencontainers/runtime-spec/specs-go // directly. message LinuxContainerResources { // CPU CFS (Completely Fair Scheduler) period. Default: 0 (not specified). int64 cpu_period = 1; // CPU CFS (Completely Fair Scheduler) quota. Default: 0 (not specified). int64 cpu_quota = 2; // CPU shares (relative weight vs. other containers). Default: 0 (not specified). int64 cpu_shares = 3; // Memory limit in bytes. Default: 0 (not specified). int64 memory_limit_in_bytes = 4; // OOMScoreAdj adjusts the oom-killer score. Default: 0 (not specified). int64 oom_score_adj = 5; // CpusetCpus constrains the allowed set of logical CPUs. Default: "" (not specified). string cpuset_cpus = 6; // CpusetMems constrains the allowed set of memory nodes. Default: "" (not specified). string cpuset_mems = 7; } // SELinuxOption are the labels to be applied to the container. message SELinuxOption { string user = 1; string role = 2; string type = 3; string level = 4; } // Capability contains the container capabilities to add or drop message Capability { // List of capabilities to add. repeated string add_capabilities = 1; // List of capabilities to drop. repeated string drop_capabilities = 2; } // LinuxContainerSecurityContext holds linux security configuration that will be applied to a container. message LinuxContainerSecurityContext { // Capabilities to add or drop. Capability capabilities = 1; // If set, run container in privileged mode. // Privileged mode is incompatible with the following options. If // privileged is set, the following features MAY have no effect: // 1. capabilities // 2. selinux_options // 4. seccomp // 5. apparmor // // Privileged mode implies the following specific options are applied: // 1. All capabilities are added. // 2. Sensitive paths, such as kernel module paths within sysfs, are not masked. // 3. Any sysfs and procfs mounts are mounted RW. // 4. Apparmor confinement is not applied. // 5. Seccomp restrictions are not applied. // 6. The device cgroup does not restrict access to any devices. // 7. All devices from the host's /dev are available within the container. // 8. SELinux restrictions are not applied (e.g. label=disabled). bool privileged = 2; // Configurations for the container's namespaces. // Only used if the container uses namespace for isolation. NamespaceOption namespace_options = 3; // SELinux context to be optionally applied. SELinuxOption selinux_options = 4; // UID to run the container process as. Only one of run_as_user and // run_as_username can be specified at a time. Int64Value run_as_user = 5; // GID to run the container process as. run_as_group should only be specified // when run_as_user or run_as_username is specified; otherwise, the runtime // MUST error. Int64Value run_as_group = 12; // User name to run the container process as. If specified, the user MUST // exist in the container image (i.e. in the /etc/passwd inside the image), // and be resolved there by the runtime; otherwise, the runtime MUST error. string run_as_username = 6; // If set, the root filesystem of the container is read-only. bool readonly_rootfs = 7; // List of groups applied to the first process run in the container, in // addition to the container's primary GID. repeated int64 supplemental_groups = 8; // AppArmor profile for the container, candidate values are: // * runtime/default: equivalent to not specifying a profile. // * unconfined: no profiles are loaded // * localhost/: profile loaded on the node // (localhost) by name. The possible profile names are detailed at // http://wiki.apparmor.net/index.php/AppArmor_Core_Policy_Reference string apparmor_profile = 9; // Seccomp profile for the container, candidate values are: // * runtime/default: the default profile for the container runtime // * unconfined: unconfined profile, ie, no seccomp sandboxing // * localhost/: the profile installed on the node. // is the full path of the profile. // Default: "", which is identical with unconfined. string seccomp_profile_path = 10; // no_new_privs defines if the flag for no_new_privs should be set on the // container. bool no_new_privs = 11; // masked_paths is a slice of paths that should be masked by the container // runtime, this can be passed directly to the OCI spec. repeated string masked_paths = 13; // readonly_paths is a slice of paths that should be set as readonly by the // container runtime, this can be passed directly to the OCI spec. repeated string readonly_paths = 14; } // LinuxContainerConfig contains platform-specific configuration for // Linux-based containers. message LinuxContainerConfig { // Resources specification for the container. LinuxContainerResources resources = 1; // LinuxContainerSecurityContext configuration for the container. LinuxContainerSecurityContext security_context = 2; } // WindowsContainerSecurityContext holds windows security configuration that will be applied to a container. message WindowsContainerSecurityContext { // User name to run the container process as. If specified, the user MUST // exist in the container image and be resolved there by the runtime; // otherwise, the runtime MUST return error. string run_as_username = 1; } // WindowsContainerConfig contains platform-specific configuration for // Windows-based containers. message WindowsContainerConfig { // Resources specification for the container. WindowsContainerResources resources = 1; // WindowsContainerSecurityContext configuration for the container. WindowsContainerSecurityContext security_context = 2; } // WindowsContainerResources specifies Windows specific configuration for // resources. message WindowsContainerResources { // CPU shares (relative weight vs. other containers). Default: 0 (not specified). int64 cpu_shares = 1; // Number of CPUs available to the container. Default: 0 (not specified). int64 cpu_count = 2; // Specifies the portion of processor cycles that this container can use as a percentage times 100. int64 cpu_maximum = 3; // Memory limit in bytes. Default: 0 (not specified). int64 memory_limit_in_bytes = 4; } // ContainerMetadata holds all necessary information for building the container // name. The container runtime is encouraged to expose the metadata in its user // interface for better user experience. E.g., runtime can construct a unique // container name based on the metadata. Note that (name, attempt) is unique // within a sandbox for the entire lifetime of the sandbox. message ContainerMetadata { // Name of the container. Same as the container name in the PodSpec. string name = 1; // Attempt number of creating the container. Default: 0. uint32 attempt = 2; } // Device specifies a host device to mount into a container. message Device { // Path of the device within the container. string container_path = 1; // Path of the device on the host. string host_path = 2; // Cgroups permissions of the device, candidates are one or more of // * r - allows container to read from the specified device. // * w - allows container to write to the specified device. // * m - allows container to create device files that do not yet exist. string permissions = 3; } // ContainerConfig holds all the required and optional fields for creating a // container. message ContainerConfig { // Metadata of the container. This information will uniquely identify the // container, and the runtime should leverage this to ensure correct // operation. The runtime may also use this information to improve UX, such // as by constructing a readable name. ContainerMetadata metadata = 1 ; // Image to use. ImageSpec image = 2; // Command to execute (i.e., entrypoint for docker) repeated string command = 3; // Args for the Command (i.e., command for docker) repeated string args = 4; // Current working directory of the command. string working_dir = 5; // List of environment variable to set in the container. repeated KeyValue envs = 6; // Mounts for the container. repeated Mount mounts = 7; // Devices for the container. repeated Device devices = 8; // Key-value pairs that may be used to scope and select individual resources. // Label keys are of the form: // label-key ::= prefixed-name | name // prefixed-name ::= prefix '/' name // prefix ::= DNS_SUBDOMAIN // name ::= DNS_LABEL map labels = 9; // Unstructured key-value map that may be used by the kubelet to store and // retrieve arbitrary metadata. // // Annotations MUST NOT be altered by the runtime; the annotations stored // here MUST be returned in the ContainerStatus associated with the container // this ContainerConfig creates. // // In general, in order to preserve a well-defined interface between the // kubelet and the container runtime, annotations SHOULD NOT influence // runtime behaviour. map annotations = 10; // Path relative to PodSandboxConfig.LogDirectory for container to store // the log (STDOUT and STDERR) on the host. // E.g., // PodSandboxConfig.LogDirectory = `/var/log/pods//` // ContainerConfig.LogPath = `containerName/Instance#.log` // // WARNING: Log management and how kubelet should interface with the // container logs are under active discussion in // https://issues.k8s.io/24677. There *may* be future change of direction // for logging as the discussion carries on. string log_path = 11; // Variables for interactive containers, these have very specialized // use-cases (e.g. debugging). // TODO: Determine if we need to continue supporting these fields that are // part of Kubernetes's Container Spec. bool stdin = 12; bool stdin_once = 13; bool tty = 14; // Configuration specific to Linux containers. LinuxContainerConfig linux = 15; // Configuration specific to Windows containers. WindowsContainerConfig windows = 16; } message CreateContainerRequest { // ID of the PodSandbox in which the container should be created. string pod_sandbox_id = 1; // Config of the container. ContainerConfig config = 2; // Config of the PodSandbox. This is the same config that was passed // to RunPodSandboxRequest to create the PodSandbox. It is passed again // here just for easy reference. The PodSandboxConfig is immutable and // remains the same throughout the lifetime of the pod. PodSandboxConfig sandbox_config = 3; } message CreateContainerResponse { // ID of the created container. string container_id = 1; } message StartContainerRequest { // ID of the container to start. string container_id = 1; } message StartContainerResponse {} message StopContainerRequest { // ID of the container to stop. string container_id = 1; // Timeout in seconds to wait for the container to stop before forcibly // terminating it. Default: 0 (forcibly terminate the container immediately) int64 timeout = 2; } message StopContainerResponse {} message RemoveContainerRequest { // ID of the container to remove. string container_id = 1; } message RemoveContainerResponse {} enum ContainerState { CONTAINER_CREATED = 0; CONTAINER_RUNNING = 1; CONTAINER_EXITED = 2; CONTAINER_UNKNOWN = 3; } // ContainerStateValue is the wrapper of ContainerState. message ContainerStateValue { // State of the container. ContainerState state = 1; } // ContainerFilter is used to filter containers. // All those fields are combined with 'AND' message ContainerFilter { // ID of the container. string id = 1; // State of the container. ContainerStateValue state = 2; // ID of the PodSandbox. string pod_sandbox_id = 3; // LabelSelector to select matches. // Only api.MatchLabels is supported for now and the requirements // are ANDed. MatchExpressions is not supported yet. map label_selector = 4; } message ListContainersRequest { ContainerFilter filter = 1; } // Container provides the runtime information for a container, such as ID, hash, // state of the container. message Container { // ID of the container, used by the container runtime to identify // a container. string id = 1; // ID of the sandbox to which this container belongs. string pod_sandbox_id = 2; // Metadata of the container. ContainerMetadata metadata = 3; // Spec of the image. ImageSpec image = 4; // Reference to the image in use. For most runtimes, this should be an // image ID. string image_ref = 5; // State of the container. ContainerState state = 6; // Creation time of the container in nanoseconds. int64 created_at = 7; // Key-value pairs that may be used to scope and select individual resources. map labels = 8; // Unstructured key-value map holding arbitrary metadata. // Annotations MUST NOT be altered by the runtime; the value of this field // MUST be identical to that of the corresponding ContainerConfig used to // instantiate this Container. map annotations = 9; } message ListContainersResponse { // List of containers. repeated Container containers = 1; } message ContainerStatusRequest { // ID of the container for which to retrieve status. string container_id = 1; // Verbose indicates whether to return extra information about the container. bool verbose = 2; } // ContainerStatus represents the status of a container. message ContainerStatus { // ID of the container. string id = 1; // Metadata of the container. ContainerMetadata metadata = 2; // Status of the container. ContainerState state = 3; // Creation time of the container in nanoseconds. int64 created_at = 4; // Start time of the container in nanoseconds. Default: 0 (not specified). int64 started_at = 5; // Finish time of the container in nanoseconds. Default: 0 (not specified). int64 finished_at = 6; // Exit code of the container. Only required when finished_at != 0. Default: 0. int32 exit_code = 7; // Spec of the image. ImageSpec image = 8; // Reference to the image in use. For most runtimes, this should be an // image ID string image_ref = 9; // Brief CamelCase string explaining why container is in its current state. string reason = 10; // Human-readable message indicating details about why container is in its // current state. string message = 11; // Key-value pairs that may be used to scope and select individual resources. map labels = 12; // Unstructured key-value map holding arbitrary metadata. // Annotations MUST NOT be altered by the runtime; the value of this field // MUST be identical to that of the corresponding ContainerConfig used to // instantiate the Container this status represents. map annotations = 13; // Mounts for the container. repeated Mount mounts = 14; // Log path of container. string log_path = 15; } message ContainerStatusResponse { // Status of the container. ContainerStatus status = 1; // Info is extra information of the Container. The key could be arbitrary string, and // value should be in json format. The information could include anything useful for // debug, e.g. pid for linux container based container runtime. // It should only be returned non-empty when Verbose is true. map info = 2; } message UpdateContainerResourcesRequest { // ID of the container to update. string container_id = 1; // Resource configuration specific to Linux containers. LinuxContainerResources linux = 2; } message UpdateContainerResourcesResponse {} message ExecSyncRequest { // ID of the container. string container_id = 1; // Command to execute. repeated string cmd = 2; // Timeout in seconds to stop the command. Default: 0 (run forever). int64 timeout = 3; } message ExecSyncResponse { // Captured command stdout output. bytes stdout = 1; // Captured command stderr output. bytes stderr = 2; // Exit code the command finished with. Default: 0 (success). int32 exit_code = 3; } message ExecRequest { // ID of the container in which to execute the command. string container_id = 1; // Command to execute. repeated string cmd = 2; // Whether to exec the command in a TTY. bool tty = 3; // Whether to stream stdin. // One of `stdin`, `stdout`, and `stderr` MUST be true. bool stdin = 4; // Whether to stream stdout. // One of `stdin`, `stdout`, and `stderr` MUST be true. bool stdout = 5; // Whether to stream stderr. // One of `stdin`, `stdout`, and `stderr` MUST be true. // If `tty` is true, `stderr` MUST be false. Multiplexing is not supported // in this case. The output of stdout and stderr will be combined to a // single stream. bool stderr = 6; } message ExecResponse { // Fully qualified URL of the exec streaming server. string url = 1; } message AttachRequest { // ID of the container to which to attach. string container_id = 1; // Whether to stream stdin. // One of `stdin`, `stdout`, and `stderr` MUST be true. bool stdin = 2; // Whether the process being attached is running in a TTY. // This must match the TTY setting in the ContainerConfig. bool tty = 3; // Whether to stream stdout. // One of `stdin`, `stdout`, and `stderr` MUST be true. bool stdout = 4; // Whether to stream stderr. // One of `stdin`, `stdout`, and `stderr` MUST be true. // If `tty` is true, `stderr` MUST be false. Multiplexing is not supported // in this case. The output of stdout and stderr will be combined to a // single stream. bool stderr = 5; } message AttachResponse { // Fully qualified URL of the attach streaming server. string url = 1; } message PortForwardRequest { // ID of the container to which to forward the port. string pod_sandbox_id = 1; // Port to forward. repeated int32 port = 2; } message PortForwardResponse { // Fully qualified URL of the port-forward streaming server. string url = 1; } message ImageFilter { // Spec of the image. ImageSpec image = 1; } message ListImagesRequest { // Filter to list images. ImageFilter filter = 1; } // Basic information about a container image. message Image { // ID of the image. string id = 1; // Other names by which this image is known. repeated string repo_tags = 2; // Digests by which this image is known. repeated string repo_digests = 3; // Size of the image in bytes. Must be > 0. uint64 size = 4; // UID that will run the command(s). This is used as a default if no user is // specified when creating the container. UID and the following user name // are mutually exclusive. Int64Value uid = 5; // User name that will run the command(s). This is used if UID is not set // and no user is specified when creating container. string username = 6; } message ListImagesResponse { // List of images. repeated Image images = 1; } message ImageStatusRequest { // Spec of the image. ImageSpec image = 1; // Verbose indicates whether to return extra information about the image. bool verbose = 2; } message ImageStatusResponse { // Status of the image. Image image = 1; // Info is extra information of the Image. The key could be arbitrary string, and // value should be in json format. The information could include anything useful // for debug, e.g. image config for oci image based container runtime. // It should only be returned non-empty when Verbose is true. map info = 2; } // AuthConfig contains authorization information for connecting to a registry. message AuthConfig { string username = 1; string password = 2; string auth = 3; string server_address = 4; // IdentityToken is used to authenticate the user and get // an access token for the registry. string identity_token = 5; // RegistryToken is a bearer token to be sent to a registry string registry_token = 6; } message PullImageRequest { // Spec of the image. ImageSpec image = 1; // Authentication configuration for pulling the image. AuthConfig auth = 2; // Config of the PodSandbox, which is used to pull image in PodSandbox context. PodSandboxConfig sandbox_config = 3; } message PullImageResponse { // Reference to the image in use. For most runtimes, this should be an // image ID or digest. string image_ref = 1; } message RemoveImageRequest { // Spec of the image to remove. ImageSpec image = 1; } message RemoveImageResponse {} message NetworkConfig { // CIDR to use for pod IP addresses. If the CIDR is empty, runtimes // should omit it. string pod_cidr = 1; } message RuntimeConfig { NetworkConfig network_config = 1; } message UpdateRuntimeConfigRequest { RuntimeConfig runtime_config = 1; } message UpdateRuntimeConfigResponse {} // RuntimeCondition contains condition information for the runtime. // There are 2 kinds of runtime conditions: // 1. Required conditions: Conditions are required for kubelet to work // properly. If any required condition is unmet, the node will be not ready. // The required conditions include: // * RuntimeReady: RuntimeReady means the runtime is up and ready to accept // basic containers e.g. container only needs host network. // * NetworkReady: NetworkReady means the runtime network is up and ready to // accept containers which require container network. // 2. Optional conditions: Conditions are informative to the user, but kubelet // will not rely on. Since condition type is an arbitrary string, all conditions // not required are optional. These conditions will be exposed to users to help // them understand the status of the system. message RuntimeCondition { // Type of runtime condition. string type = 1; // Status of the condition, one of true/false. Default: false. bool status = 2; // Brief CamelCase string containing reason for the condition's last transition. string reason = 3; // Human-readable message indicating details about last transition. string message = 4; } // RuntimeStatus is information about the current status of the runtime. message RuntimeStatus { // List of current observed runtime conditions. repeated RuntimeCondition conditions = 1; } message StatusRequest { // Verbose indicates whether to return extra information about the runtime. bool verbose = 1; } message StatusResponse { // Status of the Runtime. RuntimeStatus status = 1; // Info is extra information of the Runtime. The key could be arbitrary string, and // value should be in json format. The information could include anything useful for // debug, e.g. plugins used by the container runtime. // It should only be returned non-empty when Verbose is true. map info = 2; } message ImageFsInfoRequest {} // UInt64Value is the wrapper of uint64. message UInt64Value { // The value. uint64 value = 1; } // FilesystemIdentifier uniquely identify the filesystem. message FilesystemIdentifier{ // Mountpoint of a filesystem. string mountpoint = 1; } // FilesystemUsage provides the filesystem usage information. message FilesystemUsage { // Timestamp in nanoseconds at which the information were collected. Must be > 0. int64 timestamp = 1; // The unique identifier of the filesystem. FilesystemIdentifier fs_id = 2; // UsedBytes represents the bytes used for images on the filesystem. // This may differ from the total bytes used on the filesystem and may not // equal CapacityBytes - AvailableBytes. UInt64Value used_bytes = 3; // InodesUsed represents the inodes used by the images. // This may not equal InodesCapacity - InodesAvailable because the underlying // filesystem may also be used for purposes other than storing images. UInt64Value inodes_used = 4; } message ImageFsInfoResponse { // Information of image filesystem(s). repeated FilesystemUsage image_filesystems = 1; } message ContainerStatsRequest{ // ID of the container for which to retrieve stats. string container_id = 1; } message ContainerStatsResponse { // Stats of the container. ContainerStats stats = 1; } message ListContainerStatsRequest{ // Filter for the list request. ContainerStatsFilter filter = 1; } // ContainerStatsFilter is used to filter containers. // All those fields are combined with 'AND' message ContainerStatsFilter { // ID of the container. string id = 1; // ID of the PodSandbox. string pod_sandbox_id = 2; // LabelSelector to select matches. // Only api.MatchLabels is supported for now and the requirements // are ANDed. MatchExpressions is not supported yet. map label_selector = 3; } message ListContainerStatsResponse { // Stats of the container. repeated ContainerStats stats = 1; } // ContainerAttributes provides basic information of the container. message ContainerAttributes { // ID of the container. string id = 1; // Metadata of the container. ContainerMetadata metadata = 2; // Key-value pairs that may be used to scope and select individual resources. map labels = 3; // Unstructured key-value map holding arbitrary metadata. // Annotations MUST NOT be altered by the runtime; the value of this field // MUST be identical to that of the corresponding ContainerConfig used to // instantiate the Container this status represents. map annotations = 4; } // ContainerStats provides the resource usage statistics for a container. message ContainerStats { // Information of the container. ContainerAttributes attributes = 1; // CPU usage gathered from the container. CpuUsage cpu = 2; // Memory usage gathered from the container. MemoryUsage memory = 3; // Usage of the writeable layer. FilesystemUsage writable_layer = 4; } // CpuUsage provides the CPU usage information. message CpuUsage { // Timestamp in nanoseconds at which the information were collected. Must be > 0. int64 timestamp = 1; // Cumulative CPU usage (sum across all cores) since object creation. UInt64Value usage_core_nano_seconds = 2; } // MemoryUsage provides the memory usage information. message MemoryUsage { // Timestamp in nanoseconds at which the information were collected. Must be > 0. int64 timestamp = 1; // The amount of working set memory in bytes. UInt64Value working_set_bytes = 2; } message ReopenContainerLogRequest { // ID of the container for which to reopen the log. string container_id = 1; } message ReopenContainerLogResponse{ } sysdig-0.26.4/userspace/libsinsp/ctext.cpp000066400000000000000000000745021352731327100205710ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #ifndef _WIN32 #include "ctext.h" #include #include #include #include using namespace std; #define CTEXT_UNDER_X 0x01 #define CTEXT_OVER_X 0x02 #define CTEXT_UNDER_Y 0x04 #define CTEXT_OVER_Y 0x08 #define CTEXT_OVER (CTEXT_OVER_Y | CTEXT_OVER_X) #define CTEXT_UNDER (CTEXT_UNDER_Y | CTEXT_UNDER_X) ctext_config config_default; void search_copy(ctext_search *dst, ctext_search *src) { // Because c++ makes life impossibly difficult. memcpy(dst, src, sizeof(ctext_search) - sizeof(string)); dst->_query = src->_query; } ctext::ctext(WINDOW *win, ctext_config *config) { this->m_win = win; config_default.m_buffer_size = CTEXT_DEFAULT_BUFFER_SIZE; config_default.m_bounding_box = CTEXT_DEFAULT_BOUNDING_BOX; config_default.m_do_wrap = CTEXT_DEFAULT_DO_WRAP; config_default.m_append_top = CTEXT_DEFAULT_APPEND_TOP; config_default.m_scroll_on_append = CTEXT_DEFAULT_SCROLL_ON_APPEND; config_default.m_auto_newline = CTEXT_DEFAULT_AUTO_NEWLINE; /* this->m_debug = new ofstream(); this->m_debug->open("debug1.txt"); */ this->m_do_draw = true; if(config) { memcpy(&this->m_config, config, sizeof(ctext_config)); } else { memcpy(&this->m_config, &config_default, sizeof(ctext_config)); } this->m_pos_start.x = this->m_pos_start.y = 0; this->m_attr_mask = 0; this->m_last_search = 0; this->m_event_counter = 0; this->m_max_y = 0; // initialized the buffer with the empty row this->add_row(); } int8_t ctext::search_off() { int8_t ret = (this->m_last_search != 0); this->m_last_search = 0; return ret; } int8_t ctext::set_config(ctext_config *config) { memcpy(&this->m_config, config, sizeof(ctext_config)); return this->redraw(); } int8_t ctext::get_config(ctext_config *config) { return !memcpy(config, &this->m_config, sizeof(ctext_config)); } int8_t ctext::attach_curses_window(WINDOW *win) { this->m_win = win; return this->redraw(); } int8_t ctext::highlight(ctext_search *context, int32_t mask) { this->m_attr_mask |= mask; this->redraw_partial(&context->pos, context->_query.size()); this->m_attr_mask &= ~mask; return 0; } int8_t ctext::set_query(ctext_search *p_search, string new_query) { this->get_offset(&p_search->pos); this->get_offset(&p_search->_start_pos); p_search->_query = new_query; p_search->_last_match.y = -1; p_search->_last_event = this->m_event_counter; p_search->_match_count = 0; return 0; } ctext_search *ctext::new_search(ctext_search *you_manage_this_memory, string to_search, bool is_case_insensitive, bool is_forward, bool do_wrap) { ctext_search *p_search = you_manage_this_memory; if(!p_search) { return NULL; } p_search->is_case_insensitive = is_case_insensitive; p_search->do_wrap = do_wrap; p_search->is_forward = is_forward; this->set_query(p_search, to_search); return p_search; } int8_t ctext::highlight_matches(ctext_search *to_search) { int8_t search_ret; int32_t mask = A_BOLD | A_UNDERLINE; if(!to_search) { to_search = this->m_last_search; if(!to_search) { return 0; } } // We move the viewport pointer through the current viewport, // highlighting all matching instances. ctext_search in_viewport; ctext_pos limit; search_copy(&in_viewport, to_search); // We will say the limit is the viewport height ... this makes sure we go over // the maximum extent possible. We also make sure we do this after our first match // otherwise this would reflect our current viewport, shameful! limit.y = min(this->m_pos_start.y + this->m_win_height, (int32_t)this->m_buffer.size()); // Now we iterate through the viewport highlighting all of the instances, using the // limit and the in_viewport pointer if(this->m_event_counter != in_viewport._last_event) { search_ret = this->str_search_single(&in_viewport, &in_viewport, &limit); } do { this->highlight(&in_viewport, mask); search_ret = this->str_search_single(&in_viewport, &in_viewport, &limit); mask = A_REVERSE; } while(search_ret >= 0); return 0; } int8_t ctext::str_search(ctext_search *to_search) { int8_t search_ret, scroll_ret; this->m_last_search = to_search; // This makes sure that we scroll to a new y row // if multiple matches are on the same viewport row. for(;;) { search_ret = this->str_search_single(to_search, to_search); if(search_ret == -1) { break; } if(this->m_config.m_do_wrap) { scroll_ret = this->direct_scroll(&to_search->pos); } else { scroll_ret = this->direct_scroll(0, to_search->pos.y); } // This makes sure we move forward ... but we only do this // if we didn't push the event forward if(!scroll_ret || to_search->_match_count == 1) { break; } } // This means that it was found somewhere and our // pointer has been moved forward if(search_ret >= 0) { // We can do a general scroll_to and redraw. this->redraw(); } return search_ret; } int8_t ctext::str_search_single(ctext_search *to_search_in, ctext_search *new_pos_out, ctext_pos *limit) { int32_t size = (int32_t)this->m_buffer.size(); size_t found; string haystack; ctext_search res, *out; if(!to_search_in) { return -1; } string query = to_search_in->_query; if(!new_pos_out) { search_copy(&res, to_search_in); out = &res; } else { out = new_pos_out; } // If a (scroll) event has happened since we last ran this, // then we need to update exactly where we want to start // the search from. if(to_search_in->_last_event < this->m_event_counter) { out->pos.y = this->m_pos_start.y; out->pos.x = this->m_pos_start.x; out->_last_event = this->m_event_counter; out->_match_count = 0; } if(to_search_in->is_case_insensitive) { transform(query.begin(), query.end(), query.begin(), ::tolower); } for(;;) { haystack = this->m_buffer[out->pos.y].data; if(to_search_in->is_case_insensitive) { transform(haystack.begin(), haystack.end(), haystack.begin(), ::tolower); } if(out->is_forward) { found = haystack.find(query, (size_t)( (out->pos.x == -2) ? out->pos.x + 2 : out->pos.x + 1)); } else { found = haystack.rfind(query, (size_t)( (out->pos.x == (int32_t)haystack.size()) ? out->pos.x : out->pos.x - 1)); } if(found == string::npos) { if(out->is_forward) { out->pos.y = (out->pos.y + 1) % size; out->pos.x = -2; } else { out->pos.y--; // Wrap if we are going backwards. if(out->pos.y == -1) { out->pos.y = size - 1; } out->pos.x = (int32_t)this->m_buffer[out->pos.y].data.size(); } // // The edge case here is if there are no matches and we ARE wrapping, // we don't want the idiot case of going through the haystack endlessly // like a chump and locking up the application. // if( (out->pos.y == out->_start_pos.y && (out->do_wrap == false || out->_last_match.y == -1)) || (limit && out->pos.y > limit->y) ) { return -1; } } else { // This is all we really care about, we don't need // to look at the x value out->_last_match.y = out->pos.y; out->pos.x = (int32_t)found; out->_match_count++; break; } } return 0; } int32_t ctext::clear(int32_t row_count) { int32_t ret = 0; if(row_count == -1) { ret = this->m_buffer.size(); this->m_buffer.clear(); this->add_row(); } else if(this->m_buffer.size()) { ret = this->m_buffer.size(); this->m_buffer.erase(this->m_buffer.begin(), this->m_buffer.begin() + row_count); ret -= this->m_buffer.size(); } // We do the same logic when removing content // .. perhaps forcing things down or upward if(this->m_config.m_scroll_on_append) { this->get_win_size(); // Now we force it. this->direct_scroll(0, this->m_buffer.size() - this->m_win_height); } this->redraw(); return ret; } int8_t ctext::ob_start() { int8_t ret = this->m_do_draw; this->m_do_draw = false; return ret; } int8_t ctext::ob_end() { int8_t ret = !this->m_do_draw; this->m_do_draw = true; this->redraw(); return ret; } int8_t ctext::direct_scroll(ctext_pos*p) { return this->direct_scroll(p->x, p->y); } int8_t ctext::direct_scroll(int32_t x, int32_t y) { ctext_pos start; memcpy(&start, &this->m_pos_start, sizeof(ctext_pos)); this->get_win_size(); if(this->m_config.m_bounding_box) { y = min(y, this->m_max_y - this->m_win_height); x = max(0, x); y = max(0, y); } // Under this context we should only be x-scrolling // to a modulus of a windows width if(this->m_config.m_do_wrap) { // We always go *under* to make sure that the // content appears in the viewport. x -= x % this->m_win_width; } this->m_pos_start.x = x; this->m_pos_start.y = y; // If the values have changed and we have actively scrolled, // return 0, otherwise return -1. return (start.x != x || start.y != y) ? 0 : -1; } int8_t ctext::scroll_to(ctext_pos *pos) { return this->scroll_to(pos->x, pos->y); } int8_t ctext::scroll_to(int32_t x, int32_t y) { this->direct_scroll(x, y); this->m_event_counter++; return this->redraw(); } int8_t ctext::get_offset(ctext_pos *pos) { return this->get_offset(&pos->x, &pos->y); } int8_t ctext::get_offset(int32_t*x, int32_t*y) { *x = this->m_pos_start.x; *y = this->m_pos_start.y; return 0; } int8_t ctext::get_offset_percent(float*percent) { this->get_win_size(); *percent = (float)(this->m_pos_start.y) / (this->m_max_y - this->m_win_height); return 0; } int8_t ctext::get_buf_size(int32_t*buf_size) { *buf_size = this->m_max_y; return 0; } int32_t ctext::available_rows() { // Since our buffer clearing scheme permits us to overflow, // we have to bind this to make sure that we return >= 0 values if(this->m_config.m_buffer_size == -1) { return (int32_t)LONG_MAX; } return max(this->m_config.m_buffer_size - this->m_max_y - 1, 0); } int32_t ctext::up(int32_t amount) { return this->down(-amount); } int32_t ctext::page_down(int32_t page_count) { this->get_win_size(); return this->down(page_count * this->m_win_height); } int32_t ctext::page_up(int32_t page_count) { this->get_win_size(); return this->down(-page_count * this->m_win_height); } // Let's do this real fast. int8_t ctext::map_to_win(int32_t buffer_x, int32_t buffer_y, ctext_pos*win) { int8_t ret = 0; // This is the trivial case. if(!this->m_config.m_do_wrap) { // These are trivial. win->y = buffer_y - this->m_pos_start.y; win->x = buffer_x - this->m_pos_start.x; return ((buffer_x < this->m_pos_start.x)) | ((buffer_x > this->m_pos_start.x + this->m_win_width) << 1) | ((buffer_y < this->m_pos_start.y) << 2) | ((buffer_y > this->m_pos_start.y + this->m_win_height) << 3); } // Otherwise it's much more challenging. else { // If we are below the fold or we are at the first line and before // the start of where we ought to be drawing if(buffer_y < this->m_pos_start.y || (buffer_y == this->m_pos_start.y && buffer_x < this->m_pos_start.x)) { // We omit win calculations here since they // would be more expensive then we'd like win->x = win->y = -1; ret |= CTEXT_UNDER_Y; } else { // To see if it's an overflow y is a bit harder int32_t new_y = this->m_pos_start.y; int32_t new_offset = this->m_pos_start.x; string *data = &this->m_buffer[new_y].data; for(win->y = 0; win->y < this->m_win_height; win->y++) { new_offset += this->m_win_width; // // There's an edge case that requires this // twice due to a short circuit exit that // would be triggered at the end of a buffer, // see below. // if(buffer_y == new_y && buffer_x < new_offset) { win->x = buffer_x - (new_offset - this->m_win_width); return ret; } if(new_offset > (int32_t)data->size()) { new_offset = 0; new_y ++; if(new_y > this->m_max_y) { break; } data = &this->m_buffer[new_y].data; } if( // We've passed it and we know it's not // an underflow, so we're done. (buffer_y < new_y) || // We are at the end line and our test point // is below the offset that we just flew past (buffer_y == new_y && buffer_x < new_offset) ) { win->x = buffer_x - (new_offset - this->m_win_width); return ret; } } // Keep the y at the end // win->y = -1; // If we get here that means that we went all the way through // a "generation" and didn't reach our hit point ... that means // it's an overflow. ret |= CTEXT_OVER_Y; } } return ret; } int8_t ctext::y_scroll_calculate(int32_t amount, ctext_pos *pos) { if(this->m_config.m_do_wrap) { int32_t new_y = this->m_pos_start.y; int32_t new_offset = this->m_pos_start.x; ctext_row *p_row = &this->m_buffer[this->m_pos_start.y]; this->get_win_size(); while(amount > 0) { new_offset += this->m_win_width; amount --; if(new_offset > (int32_t)p_row->data.size()) { if((new_y + this->m_win_height + 1) >= (int32_t)this->m_buffer.size()) { // // This means that forwarding our buffer was a mistake // so we undo our work and get out of here. // new_offset -= this->m_win_width; break; } new_offset = 0; new_y++; p_row = &this->m_buffer[new_y]; } } while(amount < 0) { new_offset -= this->m_win_width; amount ++; if(new_offset < 0) { if(new_y - 1 < 0) { break; } new_y--; p_row = &this->m_buffer[new_y]; new_offset = p_row->data.size() - p_row->data.size() % this->m_win_width; } } pos->x = new_offset; pos->y = new_y; } else { pos->x = this->m_pos_start.x; pos->y = this->m_pos_start.y + amount; } return 0; } int32_t ctext::down(int32_t amount) { ctext_pos new_pos; this->y_scroll_calculate(amount, &new_pos); return this->scroll_to(&new_pos); } int32_t ctext::jump_to_first_line() { int32_t current_line = this->m_pos_start.y; // // Now we try to scroll above the first // line. the bounding box rule will // take care of the differences for us. // this->scroll_to(this->m_pos_start.x, 0 - this->m_win_height + 1); return current_line - this->m_pos_start.y; } int32_t ctext::jump_to_last_line() { int32_t current_line = this->m_pos_start.y; this->get_win_size(); this->scroll_to(this->m_pos_start.x, this->m_max_y - 1); return current_line - this->m_pos_start.y; } int32_t ctext::left(int32_t amount) { return this->right(-amount); } int32_t ctext::right(int32_t amount) { return this->scroll_to(this->m_pos_start.x + amount, this->m_pos_start.y); } void ctext::get_win_size() { int32_t width = 0, height = 0; if(this->m_win) { getmaxyx(this->m_win, height, width); } this->m_win_width = width; this->m_win_height = height; } int8_t ctext::rebuf() { // Memory management is expensive, so we only do this occasionally if(this->m_config.m_buffer_size != -1 && (int32_t)this->m_buffer.size() > (this->m_config.m_buffer_size * 11 / 10)) { this->m_buffer.erase(this->m_buffer.begin(), this->m_buffer.end() - this->m_config.m_buffer_size); } this->m_max_y = this->m_buffer.size() - 1; // // Since we've changed the bounding box of the content we have to // issue a rescroll on exactly our previous parameters. This may // force us inward or may retain our position. // return this->direct_scroll(this->m_pos_start.x, this->m_pos_start.y); } void ctext::add_format_if_needed() { attr_t attrs; int16_t color_pair; if(!this->m_win) { return; } if(this->m_buffer.empty()) { return; } // Get the most current row. ctext_row *p_row = &this->m_buffer.back(); ctext_format p_format = {0,0,0}; if(!p_row->format.empty()) { // And the most current format p_format = p_row->format.back(); } wattr_get(this->m_win, &attrs, &color_pair, 0); if(attrs != p_format.attrs || color_pair != p_format.color_pair) { // Our properties have changed so we need to record this. ctext_format new_format; // This is our offset new_format.offset = (int32_t)p_row->data.size(); new_format.attrs = attrs; new_format.color_pair = color_pair; // // If the new thing we are adding has the same // offset as the previous, then we dump the // previous. // if(p_format.offset == new_format.offset && !p_row->format.empty()) { p_row->format.pop_back(); } p_row->format.push_back(new_format); } } ctext_row* ctext::add_row() { ctext_row row; // If there is an existing line, then // we carry over the format from the // last line.. if(!this->m_buffer.empty()) { ctext_row p_row = this->m_buffer.back(); if(!p_row.format.empty()) { ctext_format p_format( p_row.format.back() ); // Set the offset to the initial. p_format.offset = 0; row.format.push_back(p_format); } } this->m_buffer.push_back(row); return &this->m_buffer.back(); } char* next_type(char* search, const char delim) { while(*search && *search != delim) { search ++; } return search; } int8_t ctext::vprintf(const char*format, va_list ap) { char *p_line, *n_line; char large_buffer[CTEXT_BUFFER_SIZE]; this->add_format_if_needed(); ctext_row *p_row = &this->m_buffer.back(); vsnprintf(large_buffer, CTEXT_BUFFER_SIZE, format, ap); p_line = large_buffer; do { n_line = next_type(p_line, '\n'); string wstr(p_line, n_line - p_line); p_row->data += wstr; if(*n_line) { p_row = this->add_row(); } p_line = n_line + 1; } while (*n_line); if(this->m_config.m_auto_newline) { this->add_row(); } // Since we are adding content we need to see if we are // to force on scroll. if(this->m_config.m_scroll_on_append) { this->get_win_size(); // Now we force it. this->direct_scroll(0, this->m_buffer.size() - this->m_win_height); } return this->redraw(); } int cprintf(ctext*win, const char *format, ...) { int ret; va_list args; va_start(args, format); ret = win->vprintf(format, args); va_end(args); return ret; } int8_t ctext::printf(const char*format, ...) { int8_t ret; va_list args; va_start(args, format); ret = this->vprintf(format, args); va_end(args); return ret; } int8_t ctext::nprintf(const char*format, ...) { int8_t ret; va_list args; // First turn off the rerdaw flag this->ob_start(); // Then call the variadic version va_start(args, format); ret = this->vprintf(format, args); va_end(args); // // Then manually untoggle the flag // (this is necessary because ob_end // does TWO things, breaking loads of // anti-patterns I'm sure.) // this->m_do_draw = true; return ret; } #if 0 int8_t ctext::redraw_partial_test() { attr_t res_attrs; int16_t res = 0; int16_t res_color_pair; int32_t x, y, end_x; string *data; this->get_win_size(); wattr_get(this->m_win, &res_attrs, &res_color_pair, 0); wattr_off(this->m_win, COLOR_PAIR(res_color_pair), 0); this->rebuf(); werase(this->m_win); x = this->m_pos_start.x; y = this->m_pos_start.y; data = &this->m_buffer[y].data; while(!(res & CTEXT_OVER_Y)) { this->m_attr_mask ^= A_REVERSE; end_x = min(x + rand() % 9 + 1, (int32_t)data->size()); res = this->redraw_partial(x, y, end_x, y); wrefresh(this->m_win); usleep(1000 * 500); x = end_x; if(end_x == (int32_t)data->size() || (res & CTEXT_OVER_X)) { y++; x = 0; if(y > this->m_max_y) { break; } data = &this->m_buffer[y].data; } } wrefresh(this->m_win); wattr_set(this->m_win, res_attrs, res_color_pair, 0); return 0; } #endif int16_t ctext::redraw_partial(ctext_pos *pos, size_t len) { return this->redraw_partial(pos->x, pos->y, pos->x + len, pos->y); } // // redraw_partial takes a buffer offset and sees if it is // to be drawn within the current view port, which is specified // by m_pos_start.x and m_pos_start.y. // int16_t ctext::redraw_partial( int32_t buf_start_x, int32_t buf_start_y, int32_t buf_end_x, int32_t buf_end_y) { bool b_format = false; string to_add; ctext_row *p_source; vector::iterator p_format; bool is_first_line = true; int16_t ret = 0; int32_t num_added_x = 0; int32_t num_to_add = 0; int32_t win_current_x; int32_t win_current_end_x; int32_t buf_offset_x; // We need to get relative start and end positions. ctext_pos win_start, win_end; buf_start_x = max(0, buf_start_x); ret = this->map_to_win(buf_start_x, buf_start_y, &win_start); // This means that none of this will map to screen, // return the overflow and bail. if(ret & CTEXT_OVER_Y) { return ret; } ret = this->map_to_win(buf_end_x, buf_end_y, &win_end); // This also means that none of this will map to screen, // return the underflow and bail. if(ret & CTEXT_UNDER_Y) { return ret; } // // We start as m_pos_start.y in our list and move up to // m_pos_start.y + m_win_height except in the case of // wrap around. Because of this special case, // we compute when to exit slightly differently. // // This is the current line of output, which stays // below m_win_height // int32_t win_current_y = win_start.y; int32_t buf_current_y = buf_start_y; // This is for horizontal scroll. int32_t start_char = max(0, this->m_pos_start.x); while(win_current_y <= win_end.y) { win_current_end_x = this->m_win_width; // If we are at the last line to generate if(win_current_y == win_end.y) { // Then we make sure that we end // where we are supposed to. win_current_end_x = win_end.x; } wredrawln(this->m_win, win_current_y, 1); if((buf_current_y < this->m_max_y) && (buf_current_y >= 0)) { // We only buf_current_y into the object if we have the // data to do so. p_source = &this->m_buffer[buf_current_y]; p_format = p_source->format.begin(); // Reset the offset. win_current_x = -min(0, (int32_t)this->m_pos_start.x); if(is_first_line) { buf_offset_x = buf_start_x; win_current_x += win_start.x; } else { buf_offset_x = start_char; } for(;;) { // Our initial num_to_add is the remainder of window space // - our start (end of the screen - starting position) num_to_add = win_current_end_x - win_current_x; b_format = false; wstandend(this->m_win); // If we have a format to account for and we haven't yet, if(!p_source->format.empty() && p_format->offset <= buf_offset_x) { // Then we add it wattr_set(this->m_win, p_format->attrs | this->m_attr_mask, p_format->color_pair, 0); // and tell ourselves below that we've done this. b_format = true; // see if there's another num_to_add point if((p_format + 1) != p_source->format.end()) { // // If it's before our newline then we'll have to do something // with with that. // // The first one is the characters we are to print this time, // the second is how many characters we would have asked for // if there was no format specified. // num_to_add = min((p_format + 1)->offset - buf_offset_x, num_to_add); } } else if(this->m_attr_mask) { wattr_set(this->m_win, this->m_attr_mask, 0, 0); } // // If we can get that many characters than we grab them // otherwise we do the empty string // if(buf_offset_x < (int32_t)p_source->data.size()) { to_add = p_source->data.substr(buf_offset_x, num_to_add); mvwaddstr(this->m_win, win_current_y, win_current_x, to_add.c_str()); is_first_line = false; } else { to_add = ""; } // This is the number of characters we've placed into // the window. num_added_x = to_add.size(); buf_offset_x += num_added_x; // See if we need to reset our format if(b_format) { // // If the amount of data we tried to grab is less than // the width of the window - win_offset then we know to // turn off our attributes and push our format forward // if necessary. // if( (p_format + 1) != p_source->format.end() && (p_format + 1)->offset >= buf_offset_x ) { p_format ++; } } // if we are at the end of the string, we break out if((int32_t)p_source->data.size() <= buf_offset_x || (num_added_x == 0 && p_source->data.size() > 0)) { break; } // Otherwise, move win_current_x forward win_current_x += num_added_x; // Otherwise, if we are wrapping, then we do that here. if(win_current_x == win_current_end_x) { // // If we've hit the vertical bottom // of our window then we break out // of this // // Otherwise if we are not wrapping then // we also break out of this. // if(win_current_y == win_end.y) { break; } // Otherwise move our line forward win_current_y++; // If we are at the last line to generate if(win_current_y == win_end.y) { // Then we make sure that we end // where we are supposed to. win_current_end_x = win_end.x; } // We reset the win_current_x back to its // initial state win_current_x = 0; // and we loop again. } } } buf_current_y++; win_current_y++; } return ret; } int8_t ctext::redraw() { // // Bail out if we aren't supposed to draw // this time. // // Calculate the bounds of everything first. // this->rebuf(); if(!this->m_do_draw) { return 0; } if(!this->m_win) { // Not doing anything without a window. return -1; } attr_t res_attrs; int16_t res_color_pair; bool is_first_line = true; wattr_get(this->m_win, &res_attrs, &res_color_pair, 0); wattr_off(this->m_win, COLOR_PAIR(res_color_pair), 0); this->get_win_size(); // // By this time, if we are bounded by a box, // it has been accounted for. // // Really our only point of interest is // whether we need to append to bottom // or append to top. // // We will assume that we can // populate the window quick enough // to avoid linear updating or paging. // ... it's 2015 after all. // werase(this->m_win); // // Regardless of whether this is append to top // or bottom we generate top to bottom. // int32_t start_char = max(0, this->m_pos_start.x); int32_t buf_offset = start_char; // the endchar will be in the substr // // We start as m_pos_start.y in our list and move up to // m_pos_start.y + m_win_height except in the case of // wrap around. Because of this special case, // we compute when to exit slightly differently. // // This is the current line of output, which stays // below m_win_height // int32_t line = 0; // Start at the beginning of the buffer. int32_t index = this->m_pos_start.y; int32_t directionality = +1; int32_t cutoff; int32_t num_added = 0; int32_t win_offset = 0; bool b_format = false; string to_add; ctext_row *p_source; vector::iterator p_format; // If we are appending to the top then we start // at the end and change our directionality. if(this->m_config.m_append_top) { directionality = -1; index = this->m_pos_start.y + this->m_win_height - 1; } while(line <= this->m_win_height) { wredrawln(this->m_win, line, 1); if((index < this->m_max_y) && (index >= 0)) { // We only index into the object if we have the // data to do so. p_source = &this->m_buffer[index]; p_format = p_source->format.begin(); // Reset the offset. win_offset = -min(0, (int32_t)this->m_pos_start.x); buf_offset = start_char; if(this->m_config.m_do_wrap) { buf_offset = is_first_line ? this->m_pos_start.x : 0; } for(;;) { // Our initial cutoff is the remainder of window space // - our start cutoff = this->m_win_width - win_offset; b_format = false; wstandend(this->m_win); // If we have a format to account for and we haven't yet, if(!p_source->format.empty() && p_format->offset <= buf_offset) { // then we add it wattr_set(this->m_win, p_format->attrs, p_format->color_pair, 0); // and tell ourselves below that we've done this. b_format = true; // See if there's another cutoff point if((p_format + 1) != p_source->format.end()) { // // If it's before our newline then we'll have to do something // with with that. // // The first one is the characters we are to print this time, // the second is how many characters we would have asked for // if there was no format specified. // cutoff = min((p_format + 1)->offset - buf_offset, cutoff); } } // If we can get that many characters than we grab them // otherwise we do the empty string if(buf_offset < (int32_t)p_source->data.size()) { to_add = p_source->data.substr(buf_offset, cutoff); mvwaddstr(this->m_win, line, win_offset, to_add.c_str()); is_first_line = false; } else { to_add = ""; } // This is the number of characters we've placed into // the window. num_added = to_add.size(); buf_offset += num_added; // See if we need to reset our format if(b_format) { // // If the amount of data we tried to grab is less than // the width of the window - win_offset then we know to // turn off our attributes and push our format forward if // necessary. // if( (p_format + 1) != p_source->format.end() && (p_format + 1)->offset >= buf_offset ) { p_format ++; } } // If we are at the end of the string, we break out if((int32_t)p_source->data.size() <= buf_offset || (num_added == 0 && p_source->data.size() > 0)) { break; } // otherwise, move win_offset forward win_offset += num_added; // otherwise, if we are wrapping, then we do that here. if(win_offset == this->m_win_width) { // // If we've hit the vertical bottom // of our window then we break out // of this otherwise if we are not // wrapping then we also break out // of this. // if(line == this->m_win_height || !this->m_config.m_do_wrap) { break; } // Otherwise move our line forward line++; // We reset the win_offset back to its // initial state win_offset = 0; // And we loop again. } } } index += directionality; line++; } this->highlight_matches(); wrefresh(this->m_win); wattr_set(this->m_win, res_attrs, res_color_pair, 0); return 0; } #endif // _WIN32 sysdig-0.26.4/userspace/libsinsp/ctext.h000066400000000000000000000343401352731327100202320ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #ifndef _WIN32 #include #include #include #include #include #include #ifndef __83a9222a_c8b9_4f36_9721_5dfbaccb28d0_CTEXT #define __83a9222a_c8b9_4f36_9721_5dfbaccb28d0_CTEXT #define CTEXT_BUFFER_SIZE (4096) using namespace std; class ctext; struct ctext_config_struct { // // This specifies how many lines are kept // in the ring-buffer. // // A value of -1 means to keep it completely // unregulated. // int32_t m_buffer_size; #define CTEXT_DEFAULT_BUFFER_SIZE 500 // // The bounding box bool specifies whether // we are allowed to move outside where the // content exists. Pretend there's the // following content and window (signified // by the + marks) // // + + // xxxx // xxx // xxxxx // + + // // If we were to move say, 3 units right and // had no bounding box (false) you'd see this: // // + + // x // // xx // + + // // In other words, we could potentially scroll // well beyond our content. // // A bounding box set to true would prevent this, // making sure that the viewport doesn't extend // beyond the existing content. // bool m_bounding_box; #define CTEXT_DEFAULT_BOUNDING_BOX false // // Sometimes content can be extremely lengthy // on one line, overwhelming any other content // in view, losing the context of what the content // means. // // In these cases, we can truncate long text from // occupying the next row of text, and instead extend // beyond the viewport of our window. Under these cases // the user will have to scroll the viewport in order // to see the remainder of the text. // bool m_do_wrap; #define CTEXT_DEFAULT_DO_WRAP false // // In most user interfaces, new text appears // underneath previous text on a new line. // // However, sometimes it's more natural to see // new entries come in ON TOP of old ones, pushing // the old ones downward. // // m_append_top deals with this duality // bool m_append_top; #define CTEXT_DEFAULT_APPEND_TOP false // // Sometimes seeing new content is of the utmost // importance and takes precendence over analysis // of any historical data. // // In that case, the scroll_on_append will forcefully // scroll the text so that the new content is // visible. // bool m_scroll_on_append; #define CTEXT_DEFAULT_SCROLL_ON_APPEND false // // The auto_newline boolean will specify whether // a newline is appended at the end of every printf // call automatically for you ... as opposed to // the more traditional printf where it is not. // // Our definition of newline is unixes, that is // the single character of 0x0A, or \n. // bool m_auto_newline; #define CTEXT_DEFAULT_AUTO_NEWLINE false }; typedef struct ctext_config_struct ctext_config; typedef struct ctext_format_struct { int32_t offset; attr_t attrs; int16_t color_pair; } ctext_format; typedef struct ctext_pos_struct { int32_t x; int32_t y; } ctext_pos; typedef struct ctext_search_struct { // The current position of the // search. ... you could do // // ct.get_offset(&search.pos); // // in order to initialize it to // the current point. // ctext_pos pos; // Should we wrap around when // we are done. bool do_wrap; // True if we are searching forward // false if we aren't. bool is_forward; // Case insensitivity is defined in the // classic (c >= 'A' ? c | 0x20) manner. bool is_case_insensitive; // This is used internally, // please don't modify. ctext_pos _start_pos; ctext_pos _last_match; uint64_t _last_event; int16_t _match_count; // The string to match string _query; } ctext_search; typedef struct ctext_row_struct { string data; vector format; } ctext_row; typedef vector ctext_buffer; class ctext { public: ctext(WINDOW *win = 0, ctext_config *config = 0); // // A ctext instance has a configuration specified through // the ctext_config structure above // // When this function is called, a copy of the structure // is made so that further modifications are not reflected // in a previously instantiated instance. // // Returns 0 on success // int8_t set_config(ctext_config *config); // // get_config allows you to change a parameter in the // configuration of a ctext instance and to duplicate // an existing configuration in a new instance. // // Returns 0 on success // int8_t get_config(ctext_config *config); // // At most 1 curses window may be attached at a time. // // This function specifies the curses window which // will be attached given this instance. // // If one is already attached, it will be detached and // potentially orphaned. // // Returns 0 on success // int8_t attach_curses_window(WINDOW *win); // // Under normal circumstances, a user would like to // remove all the existing content with a clear, starting // anew. // // However, if you'd like to only remove part of the content, // then you can pass a row_count in and clear will truncate // row_count units of the oldest content. // // The return code is how many rows were cleared from the // buffer. // int32_t clear(int32_t row_count = -1); // // Scroll_to when appending to the bottom in the traditional // default sense, will specify the top x and y coordinate // of the viewport. // // However, if we are appending to the top, then since new // content goes above the previous one, scroll_to specifies // the lower left coordinate. // // Returns 0 on success // int8_t scroll_to(int32_t x, int32_t y); int8_t scroll_to(ctext_pos *pos); // get_offset returns the current coordinates of the view port. // The values from get_offset are complementary to those // of scroll_to // // Returns 0 on success // int8_t get_offset(int32_t*x, int32_t*y); int8_t get_offset(ctext_pos *pos); // // get_offset_percent is a courtesy function returning // a percentage value corresponding to the Y amount of // scroll within the window. // // Returns 0 on success // int8_t get_offset_percent(float*percent); // // get_buf_size returns the number of rows of content // for y in the current buffer. // // Returns 0 on success // int8_t get_buf_size(int32_t*buf_size); // // available_rows communicates how many rows // are left given the buffer size specified // in the config with respect to the amount // of content placed in the buffer. // // If that sounds overly complex, I assure // you this function does pretty much exactly // what you'd expect ... it tells you how // much space you have left in the buffer // before its full and starts dropping // content. // // Returns number of available rows // int32_t available_rows(); // // Each of the directional functions, // up, down, left, and right, can be // called without an argument to move // 1 unit in their respective direction. // // The return code is how far the movement // happened. // int32_t up(int32_t amount = 1); int32_t down(int32_t amount = 1); int32_t left(int32_t amount = 1); int32_t right(int32_t amount = 1); // // Identical to the above functions but this // time by an entire page of content (that // is to say, the height of the current curses // window.) // int32_t page_up(int32_t page_count = 1); int32_t page_down(int32_t page_count = 1); // // The jump_to_first_line and jump_to_last_line // can conveniently be mapped to home/end keys // and do what they say under the following // condition: // // If the bounding_box is set to true, // then the "first" and "last" line corresponds // to an entire screen full of data. // // If the bounding box is set to false, then // the screen will be empty except for 1 line // corresponding to the first or last line. // // That is to say that with a bounding box off, // you'd see something like // // + + // // // xxxxx // + + // // when we are doing jump_to_first_line. // // With a bounding_box on you'd see // // + + // xxxxx // xx // xxx // + + // // The return code is how many vertical lines // were scrolled in order to accomplish the // action. // int32_t jump_to_first_line(); int32_t jump_to_last_line(); // // printf is identical to printf(3) and can be called // from the function at the end of this file, cprintf, // with an instance variable. It places text into the // buffer specified. // // You can take the function pointer of printf from // an existing application and point it to this printf // inside of an instance in order to migrate an existing // application to this library seamlessly. // int8_t printf(const char*format, ...); int8_t vprintf(const char*format, va_list ap); // // nprintf is identical to the printf above EXCEPT for // the fact that it doesn't refresh (redraw) the screen. // // In order to do that, a redraw (below) must be called // manually. // int8_t nprintf(const char*format, ...); // // Under normal (printf) conditions, this does not // need to be called explicitly and is instead called // each time a printf is called. // int8_t redraw(); // // A naming convention inspired from php's ob_start, // this function stops refreshing the screen until // ob_end is called, upon which a refresh is done. // // Internally, a binary flag is flipped. That is // to say that multiple ob_start calls will only // set the flag to TRUE, all to be undone by a single // ob_end call. // // Returns 0 if the call was meaningful (that is, // it toggled state) - otherwise -1. // int8_t ob_start(); int8_t ob_end(); // // This highlights a search context given a mask. // A few big mask options are A_REVERSE, A_UNDERLINE, // A_BLINK, and A_BOLD. They can be binary ORed. // int8_t highlight(ctext_search *context = 0, int32_t mask = A_REVERSE); // // This is how you initialize a search. // // You are free to toggle the properties of the object // at your own pleasure or peril. // // Returns you_manage_this_memory back at you or NULL // on an error. // ctext_search *new_search(ctext_search *you_manage_this_memory, string to_search, bool is_case_insensitive = false, bool is_forward = true, bool do_wrap = false); // // If you want to modify the query of an existing search then you // should call this function directly instead of trying to modify // the parameter yourself. // // Returns 0 on success // int8_t set_query(ctext_search *p_search, string new_query); // // After you've initiated your search you can then go over // the body of text by re-executing the str_search function. // // You don't have to worry about incrementing any silly variables // to avoid an infinite loop on the previous match or any of those // annoyances that the base c/c++ libraries decided to make YOUR // problem every time. // // This function will go to the "next" match (based on your parameters) // and then highlight all the matches in the viewport. You can // "turn off" this highlighting by running search_off (see below). // // Returns 0 every time a valid search is found and something // "happened". Otherwise you get something non-zero, signifying that // the search is "done". // int8_t str_search(ctext_search *to_search); // Turn off syntax highlighting from search. int8_t search_off(); private: // // This function answers the question "where on the screen would // the buffer at line X, character Y appear?" The *win gets populated // with the answer or a value is returned if there's an overflow. // int8_t map_to_win(int32_t buffer_x, int32_t buffer_y, ctext_pos *win); int8_t y_scroll_calculate(int32_t amount, ctext_pos *pos); int16_t redraw_partial(int32_t buf_start_x, int32_t buf_start_y, int32_t buf_end_x, int32_t buf_end_y); int16_t redraw_partial(ctext_pos *pos, size_t len); // This is just a test function to make sure everything works. int8_t redraw_partial_test(); ctext_row* add_row(); void add_format_if_needed(); int8_t rebuf(); void get_win_size(); // Highlights the matches in he current vieport without // doing any scrolling. int8_t highlight_matches(ctext_search *context = 0); // // Directly scroll to an x/y location with respect to // the buffer without any redraw or other calculation. // // This just moves the internal pointers forward with // respect to the internal configuration. // // The return value is 0 iff the value of the scroll // was changed. Otherwise, if nothing changed in the // request, -1 is returned. // int8_t direct_scroll(int32_t x, int32_t y); int8_t direct_scroll(ctext_pos *pos); // A mast to apply to the text being rendered. attr_t m_attr_mask; // // Leave the new_pos_out as null for an idempotent version of this function - // as in one that doesn't modify the to_search_in variable in returning a value. // // It's perfectly acceptable to pass the same variable as both to_search_in and // new_pos_out if you want to execute it with a side-effect - as much of the // implementation actually does. // int8_t str_search_single(ctext_search *to_search_in, ctext_search *new_pos_out = 0, ctext_pos *limit = 0); // Whether or not to draw when new text comes in or to skip the step. bool m_do_draw; WINDOW *m_win; ctext_config m_config; ctext_buffer m_buffer; ctext_search *m_last_search; // The start point of the buffer with // respect to the current viewport ctext_pos m_pos_start; int32_t m_max_y; int32_t m_win_width; int32_t m_win_height; uint64_t m_event_counter; ofstream *m_debug; }; int cprintf(ctext*win, const char *format, ...); #endif #endif // _WIN32 sysdig-0.26.4/userspace/libsinsp/cursescomponents.cpp000066400000000000000000001266211352731327100230540ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #ifndef _WIN32 #include #include #endif #include #include #include #include #include #include using namespace std; #include "sinsp.h" #include "sinsp_int.h" #include "../../driver/ppm_ringbuffer.h" #include "filter.h" #include "filterchecks.h" #ifdef CSYSDIG #include "table.h" #include "cursescomponents.h" #include "cursestable.h" #include "viewinfo.h" #include "cursesui.h" #include "utils.h" extern bool g_filterchecks_force_raw_times; /////////////////////////////////////////////////////////////////////////////// // spy_text_renderer implementation /////////////////////////////////////////////////////////////////////////////// spy_text_renderer::spy_text_renderer(sinsp* inspector, sinsp_cursesui* parent, int32_t viz_type, sysdig_output_type sotype, bool print_containers, sinsp_evt::param_fmt text_fmt) { m_formatter = NULL; m_inspector = inspector; m_viz_type = viz_type; m_linecnt = 0; g_filterchecks_force_raw_times = false; // // visualization-type inits // if(m_viz_type == VIEW_ID_DIG) { if(sotype == spy_text_renderer::OT_LATENCY) { if(print_containers) { m_formatter = new sinsp_evt_formatter(m_inspector, "*(latency=%evt.latency.human) (fd=%fd.name) %evt.num %evt.time %evt.cpu %container.name (%container.id) %proc.name (%thread.tid:%thread.vtid) %evt.dir %evt.type %evt.info"); } else { m_formatter = new sinsp_evt_formatter(m_inspector, "*(latency=%evt.latency.human) (fd=%fd.name) %evt.num %evt.time %evt.cpu %proc.name %thread.tid %evt.dir %evt.type %evt.info"); } } else if(sotype == spy_text_renderer::OT_LATENCY_APP) { if(print_containers) { m_formatter = new sinsp_evt_formatter(m_inspector, "*(latency=%tracer.latency.human) %evt.num %evt.time %evt.cpu %container.name (%container.id) %proc.name (%thread.tid:%thread.vtid) %evt.dir %evt.type %evt.info"); } else { m_formatter = new sinsp_evt_formatter(m_inspector, "*(latency=%tracer.latency.human) %evt.num %evt.time %evt.cpu %proc.name %thread.tid %evt.dir %evt.type %evt.info"); } } else { if(print_containers) { m_formatter = new sinsp_evt_formatter(m_inspector, "*%evt.num %evt.time %evt.cpu %container.name (%container.id) %proc.name (%thread.tid:%thread.vtid) %evt.dir %evt.type %evt.info"); } else { m_formatter = new sinsp_evt_formatter(m_inspector, DEFAULT_OUTPUT_STR); } } } else { m_formatter = NULL; } m_inspector->set_buffer_format(text_fmt); } spy_text_renderer::~spy_text_renderer() { if(m_formatter) { delete m_formatter; } } const char* spy_text_renderer::process_event_spy(sinsp_evt* evt, int64_t* len) { // // Drop any non I/O event // ppm_event_flags eflags = evt->get_info_flags(); if(!(eflags & EF_READS_FROM_FD || eflags & EF_WRITES_TO_FD)) { return NULL; } // // Get and validate the length // sinsp_evt_param* parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); *len = *(int64_t*)parinfo->m_val; if(*len <= 0) { return NULL; } // // Get thread and fd // sinsp_threadinfo* m_tinfo = evt->get_thread_info(); if(m_tinfo == NULL) { return NULL; } sinsp_fdinfo_t* m_fdinfo = evt->get_fd_info(); if(m_fdinfo == NULL) { return NULL; } string fdname = m_fdinfo->m_name; if(fdname == "") { fdname = "unnamed FD"; } // // Get the buffer // const char* resolved_argstr; char* argstr; argstr = (char*)evt->get_param_value_str("data", &resolved_argstr, m_inspector->get_buffer_format()); // // Trim initial or final \n // if(argstr) { uint32_t argstrlen = strlen(argstr); if(argstrlen >= 1) { if(*argstr == '\n') { argstr++; argstrlen--; } if(argstrlen >= 1) { if(argstr[argstrlen -1] == '\n') { argstr[argstrlen - 1] = 0; } } } } return argstr; } #ifndef NOCURSESUI #include #include "ctext.h" /////////////////////////////////////////////////////////////////////////////// // curses_scrollable_list implementation /////////////////////////////////////////////////////////////////////////////// curses_scrollable_list::curses_scrollable_list() { m_selct = 0; m_firstrow = 0; m_lastrow_selected = true; } void curses_scrollable_list::sanitize_selection(int32_t datasize) { if(m_firstrow > (datasize - (int32_t)m_h + 1)) { m_firstrow = datasize - (int32_t)m_h + 1; } if(m_firstrow < 0) { m_firstrow = 0; } if(m_selct > datasize - 1) { m_selct = datasize - 1; } if(m_selct < 0) { m_selct = 0; } if(m_firstrow > m_selct) { m_firstrow = m_selct; } } void curses_scrollable_list::selection_up(int32_t datasize) { if(m_selct > 0) { if(m_selct <= (int32_t)m_firstrow) { m_firstrow--; } m_selct--; sanitize_selection(datasize); } m_lastrow_selected = false; } void curses_scrollable_list::selection_down(int32_t datasize) { if(m_selct < datasize - 1) { if(m_selct - m_firstrow > (int32_t)m_h - 3) { m_firstrow++; } m_selct++; sanitize_selection(datasize); } if(m_selct == datasize - 1) { m_lastrow_selected = true; } } void curses_scrollable_list::selection_pageup(int32_t datasize) { m_firstrow -= (m_h - 1); m_selct -= (m_h - 1); sanitize_selection(datasize); m_lastrow_selected = false; } void curses_scrollable_list::selection_pagedown(int32_t datasize) { m_firstrow += (m_h - 1); m_selct += (m_h - 1); sanitize_selection(datasize); if(m_selct == datasize - 1) { m_lastrow_selected = true; } } void curses_scrollable_list::selection_home(int32_t datasize) { m_firstrow = 0; m_selct = 0; sanitize_selection(datasize); m_lastrow_selected = false; } void curses_scrollable_list::selection_end(int32_t datasize) { m_firstrow = datasize - 1; m_selct = datasize - 1; sanitize_selection(datasize); m_lastrow_selected = true; } void curses_scrollable_list::selection_goto(int32_t datasize, int32_t row) { if(row == -1 || row >= datasize) { ASSERT(false); return; } m_firstrow = row - (m_h /2); m_selct = row; sanitize_selection(datasize); } /////////////////////////////////////////////////////////////////////////////// // curses_table_sidemenu implementation /////////////////////////////////////////////////////////////////////////////// curses_table_sidemenu::curses_table_sidemenu(sidemenu_type type, sinsp_cursesui* parent, uint32_t selct, uint32_t width) { ASSERT(parent != NULL); m_parent = parent; m_h = m_parent->m_screenh - TABLE_Y_START - 1; m_w = width; m_y_start = TABLE_Y_START; m_win = newwin(m_h, m_w, m_y_start, 0); m_selct = selct; m_selct_ori = m_selct; m_type = type; if(m_selct > (int32_t)(m_h - 2)) { m_firstrow = m_selct - (int32_t)(m_h - 2); } } curses_table_sidemenu::~curses_table_sidemenu() { delwin(m_win); } void curses_table_sidemenu::render() { int32_t j, k; ASSERT(m_entries.size() != 0); // // Render window header // wattrset(m_win, m_parent->m_colors[sinsp_cursesui::PANEL_HEADER_FOCUS]); wmove(m_win, 0, 0); for(j = 0; j < (int32_t)m_w - 1; j++) { waddch(m_win, ' '); } // white space at the right wattrset(m_win, m_parent->m_colors[sinsp_cursesui::PROCESS]); waddch(m_win, ' '); ASSERT(m_title != ""); wattrset(m_win, m_parent->m_colors[sinsp_cursesui::PANEL_HEADER_FOCUS]); mvwaddnstr(m_win, 0, 0, m_title.c_str(), m_w); // // Render the rows // for(j = m_firstrow; j < MIN(m_firstrow + (int32_t)m_h - 1, (int32_t)m_entries.size()); j++) { if(j == m_selct) { wattrset(m_win, m_parent->m_colors[sinsp_cursesui::PANEL_HIGHLIGHT_FOCUS]); } else { wattrset(m_win, m_parent->m_colors[sinsp_cursesui::PROCESS]); } // clear the line wmove(m_win, j - m_firstrow + 1, 0); for(k = 0; k < (int32_t)m_w - 1; k++) { waddch(m_win, ' '); } // add the new line mvwaddnstr(m_win, j - m_firstrow + 1, 0, m_entries.at(j).m_name.c_str(), m_w); // put sorting order indicator at the right end of this row if(m_parent->m_sidemenu_sorting_col == j) { wmove(m_win, j - m_firstrow + 1, m_w - 4); char sort_order = m_parent->m_datatable->is_sorting_ascending() ? '^' : 'V'; waddch(m_win, '('); waddch(m_win, sort_order); waddch(m_win, ')'); } // white space at the right wattrset(m_win, m_parent->m_colors[sinsp_cursesui::PROCESS]); wmove(m_win, j - m_firstrow + 1, m_w - 1); waddch(m_win, ' '); } wrefresh(m_win); } // // Update the view info page in the parent // void curses_table_sidemenu::update_view_info() { if(m_parent->m_viewinfo_page) { delete m_parent->m_viewinfo_page; ASSERT(m_selct < (int32_t)m_entries.size()); m_parent->m_viewinfo_page = new curses_viewinfo_page(m_parent, m_entries.at(m_selct).m_id, TABLE_Y_START, m_w, m_parent->m_screenh - TABLE_Y_START - 1, m_parent->m_screenw - m_w); } } // // Return true if the parent should handle the event // sysdig_table_action curses_table_sidemenu::handle_input(int ch) { int32_t prev_select; int input; switch(ch) { case KEY_F(1): case KEY_F(4): case KEY_F(5): case KEY_F(6): case KEY_F(7): case 6: return STA_NONE; case '\n': case '\r': case KEY_ENTER: ASSERT(m_selct < (int32_t)m_entries.size()); if(m_type == ST_VIEWS) { if(m_parent->m_spy_box == NULL) { m_parent->m_selected_view = m_entries.at(m_selct).m_id; } m_parent->m_selected_view_sidemenu_entry = m_selct; } else if(m_type == ST_COLUMNS) { m_parent->m_selected_view_sort_sidemenu_entry = m_selct; } else { m_parent->m_selected_action_sidemenu_entry = m_selct; } return STA_SWITCH_VIEW; case KEY_BACKSPACE: case 127: case 27: // ESC case KEY_RESIZE: ASSERT(m_selct < (int32_t)m_entries.size()); if(m_type == ST_VIEWS) { if(m_parent->m_spy_box == NULL) { m_parent->m_selected_view = m_entries.at(m_selct).m_id; } m_parent->m_selected_view_sidemenu_entry = m_selct_ori; return STA_SWITCH_VIEW; } else if(m_type == ST_COLUMNS) { m_parent->m_selected_view_sort_sidemenu_entry = m_selct_ori; return STA_DESTROY_CHILD; } else { m_parent->m_selected_action_sidemenu_entry = m_selct_ori; return STA_DESTROY_CHILD; } case KEY_UP: if(m_entries.size() == 0) { return STA_NONE; } prev_select = m_selct; selection_up((int32_t)m_entries.size()); input = getch(); if(input != -1) { return handle_input(input); } if(m_selct != prev_select) { update_view_info(); } render(); return STA_NONE; case KEY_DOWN: if(m_entries.size() == 0) { return STA_NONE; } prev_select = m_selct; selection_down((int32_t)m_entries.size()); input = getch(); if(input != -1) { return handle_input(input); } if(m_selct != prev_select) { update_view_info(); } render(); return STA_NONE; case KEY_PPAGE: if(m_entries.size() == 0) { return STA_NONE; } prev_select = m_selct; selection_pageup((int32_t)m_entries.size()); input = getch(); if(input != -1) { return handle_input(input); } if(m_selct != prev_select) { update_view_info(); } render(); return STA_NONE; case KEY_NPAGE: if(m_entries.size() == 0) { return STA_NONE; } prev_select = m_selct; selection_pagedown((int32_t)m_entries.size()); input = getch(); if(input != -1) { return handle_input(input); } if(m_selct != prev_select) { update_view_info(); } render(); return STA_NONE; case KEY_HOME: if(m_entries.size() == 0) { return STA_NONE; } prev_select = m_selct; selection_home((int32_t)m_entries.size()); input = getch(); if(input != -1) { return handle_input(input); } if(m_selct != prev_select) { update_view_info(); } render(); return STA_NONE; case KEY_END: if(m_entries.size() == 0) { return STA_NONE; } prev_select = m_selct; selection_end((int32_t)m_entries.size()); input = getch(); if(input != -1) { return handle_input(input); } if(m_selct != prev_select) { update_view_info(); } render(); return STA_NONE; case KEY_MOUSE: { if(m_entries.size() == 0) { return STA_NONE; } if(getmouse(&m_last_mevent) == OK) { // // Bottom menu clicks are handled by the parent // if((uint32_t)m_last_mevent.y == m_parent->m_screenh - 1) { return STA_PARENT_HANDLE; } if(m_last_mevent.bstate & BUTTON1_CLICKED) { if((uint32_t)m_last_mevent.y > TABLE_Y_START && (uint32_t)m_last_mevent.y < TABLE_Y_START + m_h - 1) { // // This is a click one of the menu entries. Update the selection. // m_selct = m_firstrow + (m_last_mevent.y - TABLE_Y_START - 1); sanitize_selection((int32_t)m_entries.size()); update_view_info(); render(); } } else if(m_last_mevent.bstate & BUTTON1_DOUBLE_CLICKED) { if((uint32_t)m_last_mevent.y > TABLE_Y_START && (uint32_t)m_last_mevent.y < TABLE_Y_START + m_h - 1) { // // This is a double click one of the menu entries. // Update the selection. // m_selct = m_firstrow + (m_last_mevent.y - TABLE_Y_START - 1); sanitize_selection((int32_t)m_entries.size()); render(); // // This delay is here just as a lazy way to give the user the // feeling that the row has been clicked // usleep(200000); // // Notify the parent that a selection has happened // ASSERT(m_selct < (int32_t)m_entries.size()); if(m_parent->m_spy_box == NULL) { m_parent->m_selected_view = m_entries.at(m_selct).m_id; } if(m_type == ST_VIEWS) { m_parent->m_selected_view_sidemenu_entry = m_selct; } else if(m_type == ST_COLUMNS) { m_parent->m_selected_view_sort_sidemenu_entry = m_selct; } else { m_parent->m_selected_action_sidemenu_entry = m_selct; } return STA_SWITCH_VIEW; } } } } return STA_NONE; default: break; } return STA_PARENT_HANDLE; } /////////////////////////////////////////////////////////////////////////////// // curses_textbox implementation /////////////////////////////////////////////////////////////////////////////// curses_textbox::curses_textbox(sinsp* inspector, sinsp_cursesui* parent, int32_t viz_type, spy_text_renderer::sysdig_output_type sotype) { ASSERT(inspector != NULL); ASSERT(parent != NULL); m_parent = parent; m_win = NULL; m_ctext = NULL; m_filter = NULL; m_text_renderer = NULL; m_inspector = inspector; n_prints = 0; m_paused = false; m_sidemenu = NULL; m_searcher = NULL; m_has_searched = false; m_last_progress_update_ts = 0; ctext_config config; m_win = newwin(m_parent->m_screenh - 4, m_parent->m_screenw, TABLE_Y_START + 1, 0); m_ctext = new ctext(m_win); m_ctext->get_config(&config); config.m_buffer_size = 500000; config.m_scroll_on_append = true; config.m_bounding_box = true; m_text_renderer = new spy_text_renderer(inspector, parent, viz_type, sotype, m_parent->m_print_containers, sinsp_evt::PF_NORMAL); if(viz_type == VIEW_ID_DIG) { config.m_do_wrap = false; } else { config.m_do_wrap = true; } // // set the config back // m_ctext->set_config(&config); // // Allocate the searcher // m_searcher = new ctext_search(); // // If we're offline, disable screen refresh until we've parsed the file // if(!m_inspector->is_live()) { m_ctext->ob_start(); } // // Initialize the inspector to capture longer buffers and format them in a // readable way // m_inspector->set_buffer_format(sinsp_evt::PF_NORMAL); m_inspector->set_snaplen(2000); // // Tell the parent to check for input more often // m_parent->m_input_check_period_ns = 100000; // // Initial screen refresh // render(); } curses_textbox::~curses_textbox() { if(m_sidemenu) { delete m_sidemenu; } delwin(m_win); delete m_ctext; if(m_searcher) { delete m_searcher; } if(m_filter != NULL) { delete m_filter; } if(m_text_renderer) { delete m_text_renderer; } // // Restore default snaplen and output formatting // m_inspector->set_snaplen(80); m_inspector->set_buffer_format(sinsp_evt::PF_EOLS); // // Tell the parent to check for input at the usual frequency // m_parent->m_input_check_period_ns = UI_USER_INPUT_CHECK_PERIOD_NS; } void curses_textbox::set_filter(string filter) { sinsp_filter_compiler compiler(m_inspector, filter); m_filter = compiler.compile(); } void curses_textbox::print_no_data() { attrset(m_parent->m_colors[sinsp_cursesui::PROCESS]); string wstr = "No Data For This Selection"; mvprintw(m_parent->m_screenh / 2, m_parent->m_screenw / 2 - wstr.size() / 2, wstr.c_str()); refresh(); } void curses_textbox::process_event_spy(sinsp_evt* evt, int32_t next_res) { int64_t len; const char* argstr = m_text_renderer->process_event_spy(evt, &len); if(argstr == NULL) { return; } // Note: this can't be NULL because it's been validated by // m_text_renderer->process_event_spy sinsp_threadinfo* m_tinfo = evt->get_thread_info(); // // Create the info string // string info_str = "------ "; string dirstr; string cnstr; ppm_event_flags eflags = evt->get_info_flags(); if(eflags & EF_READS_FROM_FD) { dirstr = "Read "; cnstr = "from "; wattrset(m_win, m_parent->m_colors[sinsp_cursesui::SPY_READ]); } else if(eflags & EF_WRITES_TO_FD) { wattrset(m_win, m_parent->m_colors[sinsp_cursesui::SPY_WRITE]); dirstr = "Write "; cnstr = "to "; } info_str += dirstr + to_string(len) + "B " + cnstr + evt->get_fd_info()->m_name + " (" + m_tinfo->m_comm.c_str() + ")"; // // Sanitize the info string // sanitize_string(info_str); // // Print the whole thing // m_ctext->printf("%s", info_str.c_str()); if(m_parent->m_print_containers) { wattrset(m_win, m_parent->m_colors[sinsp_cursesui::LED_COLOR]); m_ctext->printf(" [%s]", m_inspector->m_container_manager.get_container_name(m_tinfo).c_str()); if(eflags & EF_READS_FROM_FD) { wattrset(m_win, m_parent->m_colors[sinsp_cursesui::SPY_READ]); } else if(eflags & EF_WRITES_TO_FD) { wattrset(m_win, m_parent->m_colors[sinsp_cursesui::SPY_WRITE]); } } m_ctext->printf("\n"); m_ctext->printf("\n"); m_ctext->printf("%s", argstr); m_ctext->printf("\n"); m_ctext->printf("\n"); n_prints++; if(n_prints == 1) { render(); } } void curses_textbox::process_event_dig(sinsp_evt* evt, int32_t next_res) { if(!m_inspector->is_debug_enabled() && evt->get_category() & EC_INTERNAL) { return; } string line; m_text_renderer->m_formatter->tostring(evt, &line); m_ctext->printf("%s\n", line.c_str()); n_prints++; if(n_prints == 1) { render(); } uint64_t ts = evt->get_ts(); if(ts > (m_last_progress_update_ts + 100000000)) { render(); m_last_progress_update_ts = ts; } } void curses_textbox::process_event(sinsp_evt* evt, int32_t next_res) { // // Check if this the end of the capture file, and if yes take note of that // if(next_res == SCAP_EOF) { m_parent->m_eof = 2; m_ctext->jump_to_first_line(); m_ctext->ob_end(); render(); if(n_prints == 0) { print_no_data(); } return; } // // If the user pressed 'p', skip the event // if(m_paused) { return; } // // Filter the event // if(m_filter) { if(!m_filter->run(evt)) { return; } } if(m_text_renderer->m_viz_type == VIEW_ID_SPY) { process_event_spy(evt, next_res); } else { process_event_dig(evt, next_res); } } void curses_textbox::populate_sidemenu() { ASSERT(m_sidemenu != NULL); m_entries.clear(); m_entries.push_back(sidemenu_list_entry("Dotted ASCII", -1)); m_entries.push_back(sidemenu_list_entry("Printable ASCII", -1)); m_entries.push_back(sidemenu_list_entry("Hex", -1)); m_sidemenu->set_entries(&m_entries); switch(m_parent->m_spybox_text_format) { case sinsp_evt::PF_NORMAL: m_sidemenu->m_selct = 0; break; case sinsp_evt::PF_EOLS: m_sidemenu->m_selct = 1; break; case sinsp_evt::PF_HEXASCII: m_sidemenu->m_selct = 2; break; case sinsp_evt::PF_JSON: m_sidemenu->m_selct = 3; break; default: ASSERT(false); m_sidemenu->m_selct = 0; break; } m_sidemenu->set_title("View As"); } void curses_textbox::render_header() { move(2, 0); attrset(m_parent->m_colors[sinsp_cursesui::FUNCTION_BAR]); for(uint32_t j = 0; j < m_parent->m_screenw; j++) { addch(' '); } refresh(); } void curses_textbox::render() { m_ctext->redraw(); render_header(); m_parent->render(); if(m_paused) { string wstr = " PAUSED "; attrset(m_parent->m_colors[sinsp_cursesui::LARGE_NUMBER]); mvprintw(0, m_parent->m_screenw / 2 - wstr.size() / 2, wstr.c_str()); } // // If required, draw the side menu // if(m_sidemenu) { m_sidemenu->render(); } } // // Return true if the parent should handle the event // sysdig_table_action curses_textbox::handle_input(int ch) { if(m_sidemenu) { sysdig_table_action ta = m_sidemenu->handle_input(ch); if(ta == STA_SWITCH_VIEW) { switch(m_parent->m_selected_view_sidemenu_entry) { case 0: m_parent->m_spybox_text_format = sinsp_evt::PF_NORMAL; break; case 1: m_parent->m_spybox_text_format = sinsp_evt::PF_EOLS; break; case 2: m_parent->m_spybox_text_format = sinsp_evt::PF_HEXASCII; break; case 3: m_parent->m_spybox_text_format = sinsp_evt::PF_JSON; break; default: ASSERT(false); break; } return STA_SWITCH_SPY; } else if(ta != STA_PARENT_HANDLE) { return STA_NONE; } } switch(ch) { case KEY_F(1): case 'q': case KEY_RESIZE: return STA_PARENT_HANDLE; case 27: // ESC case KEY_BACKSPACE: case 127: return STA_DRILLUP; case KEY_UP: m_ctext->up(); render(); return STA_NONE; case '\n': case '\r': case KEY_ENTER: case KEY_DOWN: m_ctext->down(); render(); return STA_NONE; case KEY_LEFT: m_ctext->left(); render(); return STA_NONE; case KEY_RIGHT: m_ctext->right(); render(); return STA_NONE; case KEY_PPAGE: m_ctext->page_up(); render(); return STA_NONE; case ' ': case KEY_NPAGE: m_ctext->page_down(); render(); return STA_NONE; case KEY_HOME: m_ctext->jump_to_first_line(); m_ctext->scroll_to(0, 0); render(); return STA_NONE; case KEY_END: m_ctext->jump_to_last_line(); render(); return STA_NONE; case 'c': case KEY_DC: m_ctext->clear(); render(); return STA_NONE; case 'p': if(m_inspector->is_live()) { m_paused = !m_paused; } m_ctext->jump_to_last_line(); m_parent->render(); render(); return STA_NONE; case KEY_F(2): if(m_parent->m_screenw < 20) { return STA_NONE; } if(m_sidemenu == NULL) { m_sidemenu = new curses_table_sidemenu(curses_table_sidemenu::ST_VIEWS, this->m_parent, 0, VIEW_SIDEMENU_WIDTH); populate_sidemenu(); clear(); wresize(m_win, m_parent->m_screenh - 4, m_parent->m_screenw - 20); mvwin(m_win, TABLE_Y_START + 1, 20); wrefresh(m_win); m_parent->render(); render(); m_ctext->redraw(); } else { delete m_sidemenu; m_sidemenu = NULL; wresize(m_win, m_parent->m_screenh - 4, m_parent->m_screenw); mvwin(m_win, TABLE_Y_START + 1, 0); wrefresh(m_win); m_parent->render(); render(); m_ctext->redraw(); } return STA_NONE; case '/': case KEY_F(3): on_search_next(); m_parent->render(); break; case 6: // CTRL+F m_search_type_is_goto = false; m_parent->turn_search_on(this, "Text"); break; case 7: // CTRL+G m_search_type_is_goto = true; m_parent->turn_search_on(this, "Line"); break; case KEY_MOUSE: { if(getmouse(&m_last_mevent) == OK) { return STA_PARENT_HANDLE; } } break; default: break; } return STA_NONE; } void curses_textbox::reset() { if(m_sidemenu != NULL) { delete m_sidemenu; m_sidemenu = NULL; wresize(m_win, m_parent->m_screenh - 4, m_parent->m_screenw); mvwin(m_win, TABLE_Y_START + 1, 0); wrefresh(m_win); } m_inspector->set_buffer_format(m_parent->m_spybox_text_format); // // If we're offline, disable screen refresh until we've parsed the file // if(!m_inspector->is_live()) { m_ctext->ob_start(); } // // Disable pause // m_paused = false; // // Clear the screen // m_ctext->clear(); // // Redraw everything // m_parent->render(); render(); m_ctext->redraw(); n_prints = 0; } bool curses_textbox::get_position(OUT int32_t* pos, OUT int32_t* totlines, OUT float* percent, OUT bool* truncated) { int32_t ox; m_ctext->get_offset(&ox, pos); m_ctext->get_buf_size(totlines); m_ctext->get_offset_percent(percent); *truncated = (m_ctext->available_rows() <= 0); return true; } string* curses_textbox::get_last_search_string() { return &m_last_search_string; } int8_t curses_textbox::get_offset(int32_t* x, int32_t* y) { return m_ctext->get_offset(x, y); } int8_t curses_textbox::scroll_to(int32_t x, int32_t y) { return m_ctext->scroll_to(x, y); } void curses_textbox::up() { m_ctext->up(); } bool curses_textbox::on_search_key_pressed(string search_str) { m_last_search_string = search_str; if(m_search_type_is_goto) { uint32_t line; try { line = sinsp_numparser::parseu32(search_str); } catch(...) { return false; } int32_t totlines; m_ctext->get_buf_size(&totlines); if(line > (uint32_t)totlines) { return false; } scroll_to(0, line); return true; } else { m_ctext->new_search(m_searcher, search_str, true); if(m_ctext->str_search(m_searcher) != 0) { return false; } else { m_has_searched = true; return true; } } } bool curses_textbox::on_search_next() { if(!m_has_searched) { return false; } if(m_ctext->str_search(m_searcher) != 0) { return false; } else { return true; } } /////////////////////////////////////////////////////////////////////////////// // curses_viewinfo_page implementation /////////////////////////////////////////////////////////////////////////////// curses_viewinfo_page::curses_viewinfo_page(sinsp_cursesui* parent, uint32_t viewnum, uint32_t starty, uint32_t startx, uint32_t h, uint32_t w) { m_parent = parent; ctext_config config; int input; sinsp_view_info* vinfo = parent->m_views.at(viewnum); m_win = newwin(h, w, starty, startx); m_ctext = new ctext(m_win); m_ctext->get_config(&config); config.m_buffer_size = 50000; config.m_scroll_on_append = false; config.m_bounding_box = true; config.m_do_wrap = true; parent->m_selected_view_sort_sidemenu_entry = 0; m_ctext->set_config(&config); // // Print title and info // wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("%s\n", vinfo->m_name.c_str()); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf("%s\n\n", vinfo->m_description.c_str()); // Stop and check for keyboard input input = getch(); if(input != -1) { handle_input(input); } // // Print the tips if present // if(vinfo->m_tips.size() != 0) { wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("Tips\n"); for(uint32_t j = 0; j < vinfo->m_tips.size(); j++) { wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf("%s\n\n", vinfo->m_tips[j].c_str()); } } // Stop and check for keyboard input input = getch(); if(input != -1) { handle_input(input); } // // Print columns info // wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("Columns\n"); uint32_t j; if(vinfo->get_type() == sinsp_view_info::T_TABLE) { j = vinfo->does_groupby()? 2 : 1; } else { j = 0; } for(; j < vinfo->m_columns.size(); j++) { auto c = &(vinfo->m_columns[j]); string desc; desc = c->m_description; wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("%s", c->m_name.c_str()); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": %s", desc.c_str()); m_ctext->printf("\n"); // Stop and check for keyboard input input = getch(); if(input != -1) { handle_input(input); } } m_ctext->printf("\n"); // // Print the view ID // wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("ID\n"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf("%s\n\n", vinfo->m_id.c_str()); // Stop and check for keyboard input input = getch(); if(input != -1) { handle_input(input); } // // If there's a filter, print it // if(vinfo->get_filter(m_parent->m_view_depth) != "") { wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("Filter\n"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf("%s\n\n", vinfo->get_filter(m_parent->m_view_depth).c_str()); } // // Print the actions if present // if(vinfo->m_actions.size() != 0) { wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("Action Hotkeys\n"); for(uint32_t j = 0; j < vinfo->m_actions.size(); j++) { wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("%c", vinfo->m_actions[j].m_hotkey); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": %s (%s)\n", vinfo->m_actions[j].m_description.c_str(), vinfo->m_actions[j].m_command.c_str()); } } // Stop and check for keyboard input input = getch(); if(input != -1) { handle_input(input); } // // Done. Refresh the screen // m_ctext->redraw(); } curses_viewinfo_page::~curses_viewinfo_page() { delete m_ctext; delwin(m_win); } void curses_viewinfo_page::render() { m_ctext->redraw(); } sysdig_table_action curses_viewinfo_page::handle_input(int ch) { int32_t totlines; m_ctext->get_buf_size(&totlines); if(totlines < (int32_t)m_parent->m_screenh) { return STA_DESTROY_CHILD; } switch(ch) { case KEY_UP: m_ctext->up(); render(); return STA_NONE; case '\n': case '\r': case KEY_ENTER: case KEY_DOWN: m_ctext->down(); render(); return STA_NONE; case KEY_LEFT: m_ctext->left(); render(); return STA_NONE; case KEY_RIGHT: m_ctext->right(); render(); return STA_NONE; case KEY_PPAGE: m_ctext->page_up(); render(); return STA_NONE; case ' ': case KEY_NPAGE: m_ctext->page_down(); render(); return STA_NONE; case KEY_HOME: m_ctext->jump_to_first_line(); m_ctext->scroll_to(0, 0); render(); return STA_NONE; case KEY_END: m_ctext->jump_to_last_line(); render(); return STA_NONE; default: break; } return STA_DESTROY_CHILD; } /////////////////////////////////////////////////////////////////////////////// // curses_mainhelp_page implementation /////////////////////////////////////////////////////////////////////////////// extern string g_version_string; curses_mainhelp_page::curses_mainhelp_page(sinsp_cursesui* parent) { m_parent = parent; ctext_config config; m_win = newwin(parent->m_screenh, parent->m_screenw, 0, 0); m_ctext = new ctext(m_win); m_ctext->get_config(&config); config.m_buffer_size = 50000; config.m_scroll_on_append = false; config.m_bounding_box = true; config.m_do_wrap = true; m_ctext->set_config(&config); // // Print title and info // wattrset(m_win, parent->m_colors[sinsp_cursesui::TASKS_RUNNING]); m_ctext->printf("csysdig %s. See man page for full documentation\n", g_version_string.c_str()); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf("Note: you can scroll this page by using the keyboard arrows.\n\n"); wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("How to use csysdig\n", g_version_string.c_str()); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf( "1. you can either see real time data, or analyze a trace file by using the -r command line flag.\n" "2. you can switch to a different view by using the F2 key.\n" "3. You can drill down into a selection by clicking enter. You can navigate back by typing backspace.\n" "4. you can observe reads and writes (F5) or see sysdig events (F6) for any selection.\n\n" ); wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("Drilling down\n", g_version_string.c_str()); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf( "You drill down by selecting an element in a view and then clicking enter. Once inside a selection, you can switch to a different view, and the new view will be applied in the context of the selection. For example, if you drill down into a process called foo and then switch to the Connections view, the output will include only the connections made or received by foo.\n\n" "To drill down multiple times, keep clicking enter. For example, you can click on a container in the Containers view to get the processes running inside it, and then click on one of the processes to see its threads.\n\n" ); wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("Actions and Hotkeys\n", g_version_string.c_str()); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf( "Each view has a list of command lines that can be executed in the context of the current selection by pressing 'hotkeys'. For example, pressing 'k' in the Processes view kills the selected process, pressing 'b' in the Containers view opens a bash shell in the selected container.\n" "Each view supports different actions. You can see which actions a view supports by pressing F8. You can customize the view's actions by editing the view's Lua file.\n\n" ); wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("Containers Support\n", g_version_string.c_str()); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf( "Starting csysdig with the -pc command line switch will cause many of the views to include additional container information. For example, the _Processes_ will include a column showing the container the process belongs to. Similarly, the _Connections_ view will show which container each connection belongs to.\n\n" ); // // Explore window keys // wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("\nKeyboard Shortcuts for the Views Window\n", g_version_string.c_str()); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf(" Arrows"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": scroll the table "); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("CTRL+F /"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": search\n"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf(" F2"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": switch view "); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("F4 \\"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": filter(freetext or sysdig)\n"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf(" Enter"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": drill down "); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("F10 q"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": quit\n"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("Bkspace"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": drill up "); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("DEL c"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": clear the view content\n"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf(" F5 e"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": echo FDs for selection "); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("F7"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": see info page for the selected view\n"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf(" F6 d"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": sysdig output for selection "); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("p"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": pause screen updates\n"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf(" ? F1 h"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": show this help screen "); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("F8"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": open the view's actions panel\n"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("1-9"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": sort column "); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("F9 >"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": open the column sort panel\n"); // // Text windows keys // wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("\nKeyboard Shortcuts for the 'Echo' and 'Sysdig' Windows\n", g_version_string.c_str()); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf(" Arrows"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": scroll the page "); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("CTRL+F /"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": search\n"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("Bkspace"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": drill up "); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf(" F3"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": find next\n"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf(" F2"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": choose buffer print format "); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("p"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": pause visualization\n"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf(" DEL c"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": clear the screen "); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("CTRL+G"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": go to line\n"); // // Spectrogram window keys // wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("\nKeyboard Shortcuts for the Spectrogram Window\n", g_version_string.c_str()); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf(" F2"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": switch view "); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("p"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": pause\n"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); m_ctext->printf("Bkspace"); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf(": drill up\n\n"); // // Mouse // wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("\nMouse Usage\n", g_version_string.c_str()); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf("Clicking on column headers lets you sort the table.\n" "Double clicking on row entries performs a drill down.\n" "Clicking on the filter string at the top of the screen lets you change the sysdig filter.\n" "You can use the mouse on the entries in the menu at the bottom of the screen to perform their respective actions.\n"); // // Customization // wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("\nCustomizing csysdig\n", g_version_string.c_str()); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf("csysdig is completely customizable. This means that you can modify any of the csysdig views, " "and even create your own views. Like sysdig chisels, csysdig views are Lua scripts. Full information can " "be found at the following github wiki page: https://github.com/draios/sysdig/wiki/csysdig-View-Format-Reference.\n"); // // sysdig cloud // wattrset(m_win, parent->m_colors[sinsp_cursesui::HELP_BOLD]); m_ctext->printf("\nNeed a distributed csysdig?\n", g_version_string.c_str()); wattrset(m_win, parent->m_colors[sinsp_cursesui::PROCESS]); m_ctext->printf("Sysdig cloud offers distributed csysdig functionality, a powerful web interface and much more.\nwww.sysdig.com.\n"); // // Bottom padding to compensate for a ctext bug // uint64_t trlen = ((parent->m_screenh * 230 / parent->m_screenw)) / 10; for(uint32_t j = 0; j < trlen; j++) { m_ctext->printf("\n"); } // // Done. Refresh the screen // m_ctext->redraw(); } curses_mainhelp_page::~curses_mainhelp_page() { delete m_ctext; delwin(m_win); } void curses_mainhelp_page::render() { m_ctext->redraw(); } sysdig_table_action curses_mainhelp_page::handle_input(int ch) { int32_t totlines; m_ctext->get_buf_size(&totlines); if(totlines < (int32_t)m_parent->m_screenh) { return STA_DESTROY_CHILD; } switch(ch) { case KEY_RESIZE: return STA_DESTROY_CHILD; case KEY_F(1): return STA_NONE; case 'q': case KEY_F(10): return STA_PARENT_HANDLE; case KEY_UP: m_ctext->up(); render(); return STA_NONE; case '\n': case '\r': case KEY_ENTER: case KEY_DOWN: m_ctext->down(); render(); return STA_NONE; case KEY_PPAGE: m_ctext->page_up(); render(); return STA_NONE; case ' ': case KEY_NPAGE: m_ctext->page_down(); render(); return STA_NONE; case KEY_HOME: m_ctext->jump_to_first_line(); m_ctext->scroll_to(0, 0); render(); return STA_NONE; case KEY_END: m_ctext->jump_to_last_line(); render(); return STA_NONE; default: break; } return STA_DESTROY_CHILD; } #endif // NOCURSESUI #endif // CSYSDIG sysdig-0.26.4/userspace/libsinsp/cursescomponents.h000066400000000000000000000127271352731327100225220ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ class search_caller_interface { public: virtual bool on_search_key_pressed(string search_str) = 0; virtual bool on_search_next() = 0; virtual string* get_last_search_string() = 0; }; class sidemenu_list_entry { public: sidemenu_list_entry(string name, uint32_t id) { m_name = name; m_id = id; } string m_name; uint32_t m_id; }; #ifdef CSYSDIG class sinsp_filter_check_reference; class curses_table; class sinsp_cursesui; class ctext; typedef struct ctext_search_struct ctext_search; class sinsp_evt_formatter; class spy_text_renderer { public: enum sysdig_output_type { OT_NORMAL, OT_LATENCY, OT_LATENCY_APP, }; spy_text_renderer(sinsp* inspector, sinsp_cursesui* parent, int32_t viz_type, sysdig_output_type sotype, bool print_containers, sinsp_evt::param_fmt text_fmt); ~spy_text_renderer(); const char* process_event_spy(sinsp_evt* evt, int64_t* len); sinsp_evt_formatter* m_formatter; sinsp* m_inspector; int32_t m_viz_type; uint64_t m_linecnt; }; #ifndef NOCURSESUI #define TABLE_WIDTH 10000 #define TABLE_Y_START 2 #include class sinsp_chart { public: virtual ~sinsp_chart() { } // // Returns false if this chart doesn't support returning the current position // virtual bool get_position(OUT int32_t* pos, OUT int32_t* totlines, OUT float* percent, OUT bool* truncated) = 0; }; class curses_table_column_info { public: curses_table_column_info() { } // // Use -1 as size for autosize // curses_table_column_info(IN filtercheck_field_info* info, int32_t size) { m_info = *info; m_size = size; } //private: filtercheck_field_info m_info; int32_t m_size; string m_name; friend class curses_table; }; class curses_scrollable_list { public: curses_scrollable_list(); void sanitize_selection(int32_t datasize); void selection_up(int32_t datasize); void selection_down(int32_t datasize); void selection_pageup(int32_t datasize); void selection_pagedown(int32_t datasize); void selection_home(int32_t datasize); void selection_end(int32_t datasize); void selection_goto(int32_t datasize, int32_t row); int32_t m_selct; int32_t m_selct_ori; int32_t m_firstrow; uint32_t m_w; uint32_t m_h; bool m_lastrow_selected; }; class curses_table_sidemenu : public curses_scrollable_list { public: enum sidemenu_type { ST_NONE, ST_VIEWS, ST_ACTIONS, ST_COLUMNS, }; curses_table_sidemenu(sidemenu_type type, sinsp_cursesui* parent, uint32_t selct, uint32_t width); ~curses_table_sidemenu(); void set_entries(vector* entries) { m_entries = *entries; if(m_entries.size() == 0) { m_selct = 0; } } void set_title(string title) { m_title = title; } void render(); sysdig_table_action handle_input(int ch); WINDOW* m_win; int32_t m_y_start; sinsp_cursesui* m_parent; vector m_entries; string m_title; MEVENT m_last_mevent; sidemenu_type m_type; private: void update_view_info(); }; class curses_textbox : public sinsp_chart, public search_caller_interface { public: curses_textbox(sinsp* inspector, sinsp_cursesui* parent, int32_t viz_type, spy_text_renderer::sysdig_output_type sotype); ~curses_textbox(); void render(); void set_filter(string filter); void print_no_data(); void process_event(sinsp_evt* evt, int32_t next_res); void render_header(); sysdig_table_action handle_input(int ch); void populate_sidemenu(); void reset(); bool get_position(OUT int32_t* pos, OUT int32_t* totlines, OUT float* percent, OUT bool* truncated); string* get_last_search_string(); int8_t get_offset(int32_t* x, int32_t* y); int8_t scroll_to(int32_t x, int32_t y); void up(); bool on_search_key_pressed(string search_str); bool on_search_next(); MEVENT m_last_mevent; private: inline void process_event_spy(sinsp_evt* evt, int32_t next_res); inline void process_event_dig(sinsp_evt* evt, int32_t next_res); WINDOW *m_win; ctext* m_ctext; sinsp_cursesui* m_parent; sinsp* m_inspector; sinsp_filter* m_filter; uint32_t n_prints; bool m_paused; curses_table_sidemenu* m_sidemenu; vector m_entries; // int32_t m_viz_type; // sinsp_evt_formatter* m_formatter; string m_last_search_string; ctext_search* m_searcher; bool m_has_searched; bool m_search_type_is_goto; uint64_t m_last_progress_update_ts; spy_text_renderer* m_text_renderer; }; class curses_mainhelp_page { public: curses_mainhelp_page(sinsp_cursesui* parent); ~curses_mainhelp_page(); sysdig_table_action handle_input(int ch); void render(); private: WINDOW* m_win; sinsp_cursesui* m_parent; ctext* m_ctext; }; class curses_viewinfo_page { public: curses_viewinfo_page(sinsp_cursesui* parent, uint32_t viewnum, uint32_t starty, uint32_t startx, uint32_t h, uint32_t w); ~curses_viewinfo_page(); sysdig_table_action handle_input(int ch); void render(); private: WINDOW* m_win; sinsp_cursesui* m_parent; ctext* m_ctext; }; #endif // NOCURSESUI #endif // CSYSDIG sysdig-0.26.4/userspace/libsinsp/cursesspectro.cpp000066400000000000000000000330051352731327100223370ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #ifndef _WIN32 #include #include #endif #include #include #include #include #include #include using namespace std; #include "sinsp.h" #include "sinsp_int.h" #include "../../driver/ppm_ringbuffer.h" #include "filter.h" #include "filterchecks.h" #ifdef CSYSDIG #ifndef NOCURSESUI #include #include "table.h" #include "ctext.h" #include "cursescomponents.h" #include "cursestable.h" #include "cursesspectro.h" #include "cursesui.h" // // The color palette that we will use for the chart // uint32_t g_colpalette[] = { 22, 28, 64, 34, 2, 76, 46, 118, 154, 191, 227, 226, 11, 220, 209, 208, 202, 197, 9, 1 // 17, 18, 21, 26, 27, 32, 33, 38, 39, 45, 51, 87, 159, 195, 231, 7 // 238, 241, 243, 245, 246, 247, 39, 38, 33, 32, 27, 26, 21, 18, 17 // 236, 237, 238, 239, 240, 241, 242, 243, 244,245,246,247,248,249,250,251,252,253,254, 255, // 236, 238, 240, 242, 243, 244,246,248,250,252,254,195,159,87,45, 39, 33, 27,21 }; uint32_t g_colpalette_size = sizeof(g_colpalette) / sizeof(g_colpalette[0]); /////////////////////////////////////////////////////////////////////////////// // ANSI terminal helpers /////////////////////////////////////////////////////////////////////////////// inline void ansi_movedown(int n) { printf("\033[%dE", n); } inline void ansi_moveup(int n) { printf("\033[%dF", n); } inline void ansi_hidecursor() { printf("\033[?25l"); } inline void ansi_showcursor() { printf("\033[?25h"); } inline void ansi_moveto(uint32_t y, uint32_t x) { printf("\033[%d;%dH", y, x); } inline void ansi_clearline() { printf("\033[2K"); } inline void ansi_setcolor(int col) { // Background printf("\033[48;5;%dm", col); // Foreground if(col != 0) { printf("\033[38;5;%dm", 0); } else { printf("\033[38;5;%dm", 255); } } inline void ansi_reset_color() { printf("\033[0m"); } inline void ansi_clearscreen() { printf("\033[2J"); } /////////////////////////////////////////////////////////////////////////////// // curses_spectro implementation /////////////////////////////////////////////////////////////////////////////// curses_spectro::curses_spectro(sinsp_cursesui* parent, sinsp* inspector, bool is_tracer) { m_tblwin = NULL; m_data = NULL; m_table = NULL; m_table_x_start = 0; m_table_y_start = TABLE_Y_START; m_drilled_up = false; m_selection_changed = false; m_parent = parent; m_inspector = inspector; m_converter = new sinsp_filter_check_reference(); m_n_flushes = 0; m_n_flushes_with_data = 0; m_mouse_masked = false; m_lastx = -1; m_lasty = -1; m_selstart_x = -1; m_selstart_y = -1; m_prev_sel_x1 = -1; m_prev_sel_x2 = -1; m_prev_sel_y1 = -1; m_prev_sel_y2 = -1; m_scroll_paused = false; m_is_tracer = is_tracer; m_selecting = false; // // Define the table size // m_w = m_parent->m_screenw; m_h = m_parent->m_screenh; // // Create the table window // refresh(); m_tblwin = newwin(2, m_w, m_parent->m_screenh - 3, 0); // // Put the inspector in offline replay mode // if(!m_inspector->is_live()) { parent->m_offline_replay = true; } /* for(uint32_t j = 0; j < 256; j++) { ansi_setcolor(j); printf("%d ", (int)j); } exit(0); */ } curses_spectro::~curses_spectro() { ansi_moveto(m_h, 0); printf("\n"); // // Disable offline replay mode // if(!m_inspector->is_live()) { m_parent->m_offline_replay = false; } if(m_tblwin) { delwin(m_tblwin); } delete m_converter; } void curses_spectro::configure(sinsp_table* table) { uint32_t j; m_table = table; vector* legend = m_table->get_legend(); for(j = 1; j < legend->size(); j++) { curses_table_column_info ci; ci.m_info = legend->at(j); m_legend.push_back(ci); } } void curses_spectro::print_error(string wstr) { wattrset(m_tblwin, m_parent->m_colors[sinsp_cursesui::FAILED_SEARCH]); mvwprintw(m_tblwin, m_parent->m_screenh / 2, m_parent->m_screenw / 2 - wstr.size() / 2, wstr.c_str()); } void curses_spectro::update_data(vector* data, bool force_selection_change) { m_data = data; } uint32_t curses_spectro::mkcol(uint64_t val) { uint32_t refresh_per_sec = 2; uint32_t col = log10((int)val * refresh_per_sec + 1) / log10(1.6); if(col < 1) { col = 1; } if(col > g_colpalette_size - 1) { col = g_colpalette_size - 1; } return g_colpalette[col - 1]; } void curses_spectro::draw_axis() { uint64_t x = 0; while(true) { if(x >= m_w) { break; } uint32_t curtime = (uint32_t)((double)x * 11 / m_w); uint32_t prevtime = (uint32_t)(((double)x - 1) * 11 / m_w); if(x == 0 || curtime != prevtime) { uint64_t aval = (uint64_t)pow(10, curtime); m_converter->set_val(PT_RELTIME, (uint8_t*)&aval, 8, 0, ppm_print_format::PF_DEC); string tstr = m_converter->tostring_nice(NULL, 0, 1000000000); printf("|%s", tstr.c_str()); x += tstr.size() + 1; } else { printf(" "); x++; } } } void curses_spectro::draw_menu(bool there_is_more) { printf("F1"); ansi_setcolor(24); printf("Help "); ansi_reset_color(); printf("F2"); ansi_setcolor(24); printf("Views "); ansi_reset_color(); printf("p "); ansi_setcolor(24); printf("Pause "); ansi_reset_color(); printf("BKSPACE"); ansi_setcolor(24); printf("Back"); ansi_reset_color(); printf("MOUSE"); ansi_setcolor(24); printf("DrillDown"); ansi_reset_color(); if(there_is_more) { printf("SPACE"); ansi_setcolor(24); printf("More"); ansi_reset_color(); } } void curses_spectro::render(bool data_changed) { // // Clear the screen // if(m_data == NULL) { return; } m_n_flushes++; if(m_data->size() != 0) { if(m_legend.size() != m_data->at(0).m_values.size()) { ASSERT(false); throw sinsp_exception("corrupted curses table data"); } } else { if(m_n_flushes < 2) { printf("\n"); } else { return; } } if(data_changed) { unordered_map freqs; m_n_flushes_with_data++; // // Create a map with the frequencies for every latency interval // for(auto d : *m_data) { sinsp_table_field* key = &(d.m_values[0]); sinsp_table_field* data = &(d.m_values[1]); if(key->m_len != 8) { throw sinsp_exception("the key of a spectrogram view must be a number"); } uint64_t val = *(uint64_t*)key->m_val; freqs[val] = *(uint32_t*)data->m_val; } ansi_moveto(m_h - 2, 0); // // Render the line // m_t_row.clear(m_table->m_prev_flush_time_ns); for(uint32_t j = 0; j < m_w - 1; j++) { auto it = freqs.find(j); if(it != freqs.end()) { m_t_row.push_back(it->second); uint32_t col = mkcol(it->second); ansi_setcolor(col); printf(" "); } else { m_t_row.push_back(0); ansi_setcolor(0); printf(" "); } } m_history.push_back(m_t_row); bool will_pause = !m_inspector->is_live() && m_n_flushes_with_data % (m_h - 3) == 0; ansi_reset_color(); ansi_moveto(m_h - 1, 0); draw_axis(); ansi_moveto(m_h, 0); draw_menu(will_pause); printf("\n"); if(will_pause) { m_scroll_paused = true; } } } // // Return false if the user wants us to exit // sysdig_table_action curses_spectro::handle_input(int ch) { if(m_data == NULL) { return STA_PARENT_HANDLE; } switch(ch) { case KEY_F(2): clear(); return STA_PARENT_HANDLE; case KEY_ENTER: return STA_DRILLDOWN; case KEY_BACKSPACE: case 127: return STA_DRILLUP; case KEY_MOUSE: { if(!m_parent->m_is_mousedrag_available) { string msgstr = "Mouse input not supported in this terminal"; uint32_t xs = (msgstr.size() >= m_w)? 0 : (m_w / 2) - (msgstr.size() / 2); ansi_moveto(m_h / 2, xs); printf("%s\n", msgstr.c_str()); return STA_NONE; } /* if(!m_mouse_masked) { mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL); m_mouse_masked = true; } */ if(getmouse(&m_last_mevent) == OK) { if(m_last_mevent.bstate & BUTTON1_CLICKED) { g_logger.format("mouse clicked"); if(m_last_mevent.y == (int)m_h - 2) { if(m_last_mevent.x >= 3 && m_last_mevent.x <= 7) { return m_parent->handle_input(KEY_F(1)); } else if(m_last_mevent.x >= 10 && m_last_mevent.x <= 15) { return m_parent->handle_input(KEY_F(2)); } else if(m_last_mevent.x >= 18 && m_last_mevent.x <= 23) { return m_parent->handle_input('p'); } else if(m_last_mevent.x >= 31 && m_last_mevent.x <= 34) { return STA_DRILLUP; } } else { if(m_inspector->is_live()) { break; } if(!m_selecting) { m_selecting = true; m_selstart_x = -1; m_selstart_y = -1; } else { m_selecting = false; curses_spectro_history_row* start_row = get_history_row_from_coordinate(m_selstart_y); curses_spectro_history_row* end_row = get_history_row_from_coordinate(m_prev_sel_y2 - 1); uint64_t start_latency = latency_from_coordinate(m_selstart_x); uint64_t end_latency = latency_from_coordinate(m_prev_sel_x2); if(start_row == NULL || end_row == NULL) { break; } string lat_fld_name; if(m_is_tracer) { lat_fld_name = "span.duration"; } else { lat_fld_name = "evt.latency"; } m_selection_filter = "(evt.rawtime>=" + to_string(start_row->m_ts - m_table->m_refresh_interval_ns) + " and evt.rawtime<=" + to_string(end_row->m_ts) + ") and (" + lat_fld_name + ">=" + to_string(start_latency) + " and " + lat_fld_name + "<" + to_string(end_latency) + ")"; g_logger.format("spectrogram drill down"); g_logger.format("filter: %s", m_selection_filter.c_str()); m_selstart_x = -1; m_selstart_y = -1; ansi_reset_color(); if(m_is_tracer) { return STA_DRILLDOWN; } else { return STA_DIG; } } } } else { if(m_selecting) { if((m_last_mevent.y > (int)m_h - 4) || ((int)m_last_mevent.y <= (int)m_h - 3 - (int)m_history.size())) { break; } if(m_selstart_x == -1) { m_selstart_x = m_last_mevent.x; m_selstart_y = m_last_mevent.y; } if(m_prev_sel_x1 != -1) { draw_square(m_prev_sel_y1, m_prev_sel_x1, m_prev_sel_y2, m_prev_sel_x2, ' '); } m_prev_sel_y1 = m_selstart_y; m_prev_sel_x1 = m_selstart_x; m_prev_sel_y2 = m_last_mevent.y + 1; m_prev_sel_x2 = m_last_mevent.x + 1; draw_square(m_selstart_y, m_selstart_x, m_last_mevent.y + 1, m_last_mevent.x + 1, 'X'); } } } } break; case 'c': case KEY_DC: // Del break; case KEY_F(7): return STA_NONE; default: break; } return STA_PARENT_HANDLE; } void curses_spectro::draw_square(int32_t y1, int32_t x1, int32_t y2, int32_t x2, char c) { if(x2 < x1 || y2 < y1) { return; } for(int32_t j = y1; j < y2; j++) { ansi_moveto(j + 1, x1 + 1); if(j == y1 || j == y2 - 1) { for(int32_t k = x1; k < x2; k++) { int64_t col = get_history_color_from_coordinate(j, k); if(col == -1) { break; } ansi_setcolor(col); printf("%c", c); } } else { int64_t col = get_history_color_from_coordinate(j, x1); if(col == -1) { break; } ansi_setcolor(col); printf("%c", c); col = get_history_color_from_coordinate(j, x2 - 1); if(col == -1) { break; } ansi_moveto(j + 1, x2); ansi_setcolor(col); printf("%c", c); } printf("\n"); } } int64_t curses_spectro::get_history_value_from_coordinate(uint32_t y, uint32_t x) { if((m_h - y > 3) && (m_h - y - 4) < m_history.size() - 1) { curses_spectro_history_row& row = m_history[m_history.size() - 1 - (m_h - y - 4)]; ASSERT(x >= 0); ASSERT(row.m_data.size() == m_w - 1); if(x >= row.m_data.size()) { return -1; } return row.m_data[x]; } return -1; } int64_t curses_spectro::get_history_color_from_coordinate(uint32_t y, uint32_t x) { int64_t hv = get_history_value_from_coordinate(y, x); if(hv != -1) { if(hv == 0) { return 0; } else { int64_t col = mkcol(hv); return col; } } else { return -1; } } curses_spectro_history_row* curses_spectro::get_history_row_from_coordinate(uint32_t y) { if((y <= m_h - 4) && ((int)y > (int)m_h - 3 - (int)m_history.size())) { return &(m_history[m_history.size() - 1 - (m_h - y - 4)]); } else { return NULL; } } uint64_t curses_spectro::latency_from_coordinate(uint32_t x) { double curtime = (double)x * 11 / m_w; return (uint64_t)pow(10, curtime); } void curses_spectro::recreate_win(int h) { delwin(m_tblwin); m_tblwin = newwin(m_h - 3, m_w, m_table_y_start, m_table_x_start); render(true); } #endif // NOCURSESUI #endif // CSYSDIG sysdig-0.26.4/userspace/libsinsp/cursesspectro.h000066400000000000000000000060431352731327100220060ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #ifdef CSYSDIG #ifndef NOCURSESUI class colpalette_entry { public: colpalette_entry(int color, char ch) { m_color = color; m_char = ch; } int m_color; char m_char; }; class curses_spectro_history_row { public: void clear(uint64_t ts) { m_ts = ts; m_data.clear(); } void push_back(uint32_t val) { m_data.push_back(val); } uint64_t m_ts; vector m_data; }; class curses_spectro : public sinsp_chart { public: enum alignment { ALIGN_LEFT, ALIGN_RIGHT, }; curses_spectro(sinsp_cursesui* parent, sinsp* inspector, bool is_tracer); ~curses_spectro(); void configure(sinsp_table* table); void update_data(vector* data, bool force_selection_change = false); void render(bool data_changed); sysdig_table_action handle_input(int ch); void set_x_start(uint32_t x) { m_table_x_start = x; } void recreate_win(int h); uint32_t get_data_size() { if(m_table != NULL) { return m_data->size(); } else { return 0; } } bool get_position(OUT int32_t* pos, OUT int32_t* totlines, OUT float* percent, OUT bool* truncated) { return false; } sinsp_table_field_storage m_last_key; bool m_drilled_up; bool m_selection_changed; MEVENT m_last_mevent; string m_selection_filter; bool m_scroll_paused; private: void print_error(string wstr); uint32_t mkcol(uint64_t n); void draw_axis(); void draw_menu(bool there_is_more); int64_t get_history_value_from_coordinate(uint32_t y, uint32_t x); int64_t get_history_color_from_coordinate(uint32_t y, uint32_t x); curses_spectro_history_row* get_history_row_from_coordinate(uint32_t y); uint64_t latency_from_coordinate(uint32_t x); void draw_square(int32_t y1, int32_t x1, int32_t y2, int32_t x2, char c); sinsp* m_inspector; WINDOW* m_tblwin; sinsp_cursesui* m_parent; sinsp_table* m_table; int32_t m_table_x_start; uint32_t m_table_y_start; vector m_legend; vector* m_data; uint32_t m_w; uint32_t m_h; vector m_colpalette; sinsp_filter_check_reference* m_converter; uint64_t m_n_flushes; uint64_t m_n_flushes_with_data; vector m_history; curses_spectro_history_row m_t_row; bool m_mouse_masked; int32_t m_lastx, m_lasty; int32_t m_selstart_x, m_selstart_y; int32_t m_prev_sel_x1, m_prev_sel_x2; int32_t m_prev_sel_y1, m_prev_sel_y2; bool m_is_tracer; bool m_selecting; friend class curses_spectro_sidemenu; }; #endif // NOCURSESUI #endif // CSYSDIG sysdig-0.26.4/userspace/libsinsp/cursestable.cpp000066400000000000000000000436621352731327100217610ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #ifndef _WIN32 #include #endif #include #include #include #include #include #include using namespace std; #include "sinsp.h" #include "sinsp_int.h" #include "../../driver/ppm_ringbuffer.h" #include "filter.h" #include "filterchecks.h" #ifdef CSYSDIG #ifndef NOCURSESUI #include #include "table.h" #include "cursescomponents.h" #include "cursestable.h" #include "cursesui.h" /////////////////////////////////////////////////////////////////////////////// // curses_table implementation /////////////////////////////////////////////////////////////////////////////// curses_table::curses_table(sinsp_cursesui* parent, sinsp* inspector, sinsp_table::tabletype type) { m_tblwin = NULL; m_data = NULL; m_table = NULL; m_table_x_start = 0; m_table_y_start = TABLE_Y_START; m_drilled_up = false; m_selection_changed = false; m_parent = parent; m_inspector = inspector; m_type = type; m_converter = new sinsp_filter_check_reference(); // // Column sizes initialization // m_colsizes[PT_NONE] = 0; m_colsizes[PT_INT8] = 8; m_colsizes[PT_INT16] = 8; m_colsizes[PT_INT32] = 8; m_colsizes[PT_INT64] = 8; m_colsizes[PT_UINT8] = 8; m_colsizes[PT_UINT16] = 8; m_colsizes[PT_UINT32] = 8; m_colsizes[PT_UINT64] = 8; m_colsizes[PT_CHARBUF] = 32; m_colsizes[PT_BYTEBUF] = 32; m_colsizes[PT_ERRNO] = 8; m_colsizes[PT_SOCKADDR] = 16; m_colsizes[PT_SOCKTUPLE] = 16; m_colsizes[PT_FD] = 32; m_colsizes[PT_PID] = 16; m_colsizes[PT_FDLIST] = 16; m_colsizes[PT_FSPATH] = 32; m_colsizes[PT_SYSCALLID] = 8; m_colsizes[PT_SIGTYPE] = 8; m_colsizes[PT_RELTIME] = 16; m_colsizes[PT_ABSTIME] = 16; m_colsizes[PT_PORT] = 8; m_colsizes[PT_L4PROTO] = 8; m_colsizes[PT_SOCKFAMILY] = 8; m_colsizes[PT_BOOL] = 8; m_colsizes[PT_IPV4ADDR] = 8; m_colsizes[PT_IPV6ADDR] = 16; m_colsizes[PT_DYN] = 8; m_colsizes[PT_FLAGS8] = 32; m_colsizes[PT_FLAGS16] = 32; m_colsizes[PT_FLAGS32] = 32; m_colsizes[PT_MODE] = 32; m_colsizes[PT_UID] = 12; m_colsizes[PT_GID] = 12; m_colsizes[PT_DOUBLE] = 8; m_colsizes[PT_SIGSET] = 32; // // Define the table size // m_w = TABLE_WIDTH; m_h = m_parent->m_screenh - 3; m_scrolloff_x = 0; // // Create the table window // refresh(); m_tblwin = newwin(m_h, m_w, m_table_y_start, 0); } curses_table::~curses_table() { if(m_tblwin) { delwin(m_tblwin); } delete m_converter; } void curses_table::configure(sinsp_table* table, vector* colsizes, vector* colnames) { uint32_t j; m_table = table; vector* legend = m_table->get_legend(); if(colsizes) { if(colsizes->size() != 0 && colsizes->size() != legend->size()) { throw sinsp_exception("invalid table legend for view " + m_parent->m_views.at(m_parent->m_selected_view)->m_name + " : column sizes doesn't match (" + to_string(colsizes->size()) + " column sizes, " + to_string(legend->size()) + " entries in legend)"); } } if(colnames) { if(colnames->size() != 0 && colnames->size() != legend->size()) { throw sinsp_exception("invalid table legend for view " + m_parent->m_views.at(m_parent->m_selected_view)->m_name + " : column names doesn't match (" + to_string(colnames->size()) + " column names, " + to_string(legend->size()) + " entries in legend)"); } } for(j = 1; j < legend->size(); j++) { curses_table_column_info ci; ci.m_info = legend->at(j); if(colsizes->size() == 0 || colsizes->at(j) == -1) { ci.m_size = m_colsizes[legend->at(j).m_type]; } else { ci.m_size = colsizes->at(j); } if(colnames->size() == 0) { ci.m_name = ci.m_info.m_name; } else { ci.m_name = colnames->at(j); } /* int32_t namelen = strlen(ci.m_info.m_name); if(ci.m_size < namelen + 1) { ci.m_size = namelen + 1; } */ m_legend.push_back(ci); } } void curses_table::update_rowkey(int32_t row) { sinsp_table_field* rowkey = m_table->get_row_key(row); if(rowkey != NULL) { m_last_key.copy(rowkey); m_last_key.m_isvalid = true; } else { m_last_key.m_isvalid = false; } } void curses_table::update_data(vector* data, bool force_selection_change) { m_data = data; if(m_selection_changed && (m_last_key.m_isvalid || m_drilled_up || force_selection_change)) { int32_t selct = m_table->get_row_from_key(&m_last_key); if(selct == -1) { m_selct--; m_last_key.m_isvalid = false; } else { m_selct = selct; // if(m_drilled_up) { selection_goto((int32_t)m_data->size(), m_selct); } render(true); //m_drilled_up = false; } sanitize_selection((int32_t)m_data->size()); } else { update_rowkey(m_selct); } } void curses_table::print_line_centered(string line, int32_t off) { wattrset(m_tblwin, m_parent->m_colors[sinsp_cursesui::PROCESS]); if(line.size() < m_parent->m_screenw) { mvwprintw(m_tblwin, m_parent->m_screenh / 2 + off, m_parent->m_screenw / 2 - line.size() / 2, line.c_str()); } else { uint32_t spos = 0; for(uint32_t j = 0;; j++) { string ss = line.substr(spos, spos + m_parent->m_screenw); glogf("2, %d %s\n", spos, ss.c_str()); mvwprintw(m_tblwin, m_parent->m_screenh / 2 + off + j, 0, ss.c_str()); spos += m_parent->m_screenw; if(spos >= line.size()) { break; } } } } void curses_table::print_wait() { string wstr; bool is_tracer_view = false; sinsp_view_info* vinfo = m_parent->get_selected_view(); if(vinfo) { if(vinfo->m_id == "tracers" || vinfo->m_id == "tracer_ids") { is_tracer_view = true; } } else { ASSERT(false); } if(is_tracer_view) { print_line_centered("No data for this view."); print_line_centered("Note: in order to see any data here, you need to push tracers to sysdig from your app as described here: XXX.", 2); } else { if(m_inspector->is_live()) { wstr = "Collecting Data"; } else { if(m_parent->is_eof()) { wstr = "No Data For This View"; } } print_line_centered(wstr); } } void curses_table::print_error(string wstr) { wattrset(m_tblwin, m_parent->m_colors[sinsp_cursesui::FAILED_SEARCH]); mvwprintw(m_tblwin, m_parent->m_screenh / 2, m_parent->m_screenw / 2 - wstr.size() / 2, wstr.c_str()); } void curses_table::render(bool data_changed) { uint32_t j, k; int32_t l, m; wclear(m_tblwin); // // Clear the screen // if(m_data == NULL) { print_wait(); goto render_end; } if(m_data->size() != 0) { if(m_legend.size() != m_data->at(0).m_values.size()) { ASSERT(false); throw sinsp_exception("corrupted curses table data"); } } if(data_changed) { m_column_startx.clear(); if(m_selct < 0) { m_selct = 0; } else if(m_selct > (int32_t)m_data->size() - 1) { m_selct = (int32_t)m_data->size() - 1; } wattrset(m_tblwin, m_parent->m_colors[sinsp_cursesui::PANEL_HEADER_FOCUS]); // // Render the column headers // wmove(m_tblwin, 0, 0); for(j = 0; j < m_w; j++) { if(m_type == sinsp_table::TT_TABLE) { wattrset(m_tblwin, m_parent->m_colors[sinsp_cursesui::PANEL_HEADER_FOCUS]); } else { wattrset(m_tblwin, m_parent->m_colors[sinsp_cursesui::PANEL_HEADER_LIST_FOCUS]); } waddch(m_tblwin, ' '); } for(j = 0, k = 0; j < m_legend.size(); j++) { if(j == m_table->get_sorting_col() - 1) { if(m_type == sinsp_table::TT_TABLE) { wattrset(m_tblwin, m_parent->m_colors[sinsp_cursesui::PANEL_HIGHLIGHT_FOCUS]); } else { wattrset(m_tblwin, m_parent->m_colors[sinsp_cursesui::PANEL_HEADER_LIST_HIGHLIGHT]); } } else { if(m_type == sinsp_table::TT_TABLE) { wattrset(m_tblwin, m_parent->m_colors[sinsp_cursesui::PANEL_HEADER_FOCUS]); } else { wattrset(m_tblwin, m_parent->m_colors[sinsp_cursesui::PANEL_HEADER_LIST_FOCUS]); } } m_column_startx.push_back(k); string coltext = m_legend[j].m_name; if((int32_t)coltext.size() > m_legend[j].m_size - 1) { coltext = coltext.substr(0, m_legend[j].m_size - 1); } uint32_t tindex = m_table->m_do_merging? j + 2 : j + 1; curses_table::alignment al = get_field_alignment(m_table->m_types->at(tindex)); if(al == curses_table::ALIGN_RIGHT) { coltext.insert(0, m_legend[j].m_size - coltext.size() - 1, ' '); } mvwaddnstr(m_tblwin, 0, k, coltext.c_str(), m_legend[j].m_size - 1); for(l = strlen(m_legend[j].m_name.c_str()); l < m_legend[j].m_size; l++) { waddch(m_tblwin, ' '); } k += m_legend[j].m_size; } // // If there is no data, print the "waiting for data" message // if(m_data->size() == 0) { if(!m_parent->is_searching()) { print_wait(); goto render_end; } } // // Render the rows // vector* row; for(l = 0; l < (int32_t)MIN(m_data->size(), m_h - 1); l++) { if(l + m_firstrow >= (int32_t)m_data->size()) { break; } row = &(m_data->at(l + m_firstrow).m_values); // // Pick the proper color based on the selection // if(l == m_selct - (int32_t)m_firstrow) { wattrset(m_tblwin, m_parent->m_colors[sinsp_cursesui::PANEL_HIGHLIGHT_FOCUS]); } else { wattrset(m_tblwin, m_parent->m_colors[sinsp_cursesui::PROCESS]); } // // Render the row // wmove(m_tblwin, l + 1, 0); for(j = 0; j < m_w; j++) { waddch(m_tblwin, ' '); } for(j = 0, k = 0; j < m_legend.size(); j++) { sinsp_filter_check* extractor = m_table->m_extractors->at(j + 1); uint64_t td = 0; if(extractor->m_aggregation == A_TIME_AVG || extractor->m_merge_aggregation == A_TIME_AVG) { td = m_parent->get_time_delta(); } m_converter->set_val(m_legend[j].m_info.m_type, row->at(j).m_val, row->at(j).m_len, row->at(j).m_cnt, m_legend[j].m_info.m_print_format); uint32_t size = m_legend[j].m_size - 1; // // size=0 means "use the whole available space" // if(size == 0) { size = m_w - k - 1; } mvwaddnstr(m_tblwin, l + 1, k, m_converter->tostring_nice(NULL, size, td), size); k += m_legend[j].m_size; } } wattrset(m_tblwin, m_parent->m_colors[sinsp_cursesui::PROCESS]); if(l < (int32_t)m_h - 1) { for(m = l; m < (int32_t)m_h - 1; m++) { wmove(m_tblwin, m + 1, 0); for(j = 0; j < m_w; j++) { waddch(m_tblwin, ' '); } } } } render_end: if(m_data && m_data->size() == 0) { if(m_parent->is_searching()) { print_error(string(" NO MATCH ")); } } if(m_parent->m_search_nomatch) { print_error(string(" NOT FOUND ")); } if(m_scrolloff_x != 0) { chtype chstr[m_w]; for(j = 0; j < m_h; j++) { mvwinchnstr(m_tblwin, j, 0, chstr, m_parent->m_screenw + m_scrolloff_x); mvwaddchnstr(m_tblwin, j, 0, chstr + m_scrolloff_x, m_parent->m_screenw); } } wrefresh(m_tblwin); m_parent->render(); refresh(); } string curses_table::get_field_val(string fldname) { uint32_t j; vector* row; string res; row = &(m_data->at(m_selct).m_values); vector* legend; if(m_parent->m_datatable->m_postmerge_legend.size() != 0) { legend = &m_parent->m_datatable->m_postmerge_legend; } else { legend = &m_parent->m_datatable->m_premerge_legend; } for(j = 1; j < legend->size(); j++) { auto le = legend->at(j); if(le.m_name == fldname) { uint32_t k = j - 1; m_converter->set_val(m_legend[k].m_info.m_type, row->at(k).m_val, row->at(k).m_len, row->at(k).m_cnt, m_legend[k].m_info.m_print_format); res = m_converter->tostring_nice(NULL, 0, 0); break; } } if(j == legend->size()) { throw sinsp_exception("field '" + fldname + "'' not found in this view"); } return res; } // // Return false if the user wants us to exit // sysdig_table_action curses_table::handle_input(int ch) { if(m_data == NULL) { return STA_PARENT_HANDLE; } switch(ch) { case KEY_LEFT: if(m_scrolloff_x > 0) { m_scrolloff_x -= 4; render(true); } break; case KEY_RIGHT: if(m_scrolloff_x < m_w - m_parent->m_screenw) { m_scrolloff_x += 4; render(true); } break; case KEY_UP: m_selection_changed = true; selection_up((int32_t)m_data->size()); update_rowkey(m_selct); render(true); break; case KEY_DOWN: m_selection_changed = true; selection_down((int32_t)m_data->size()); update_rowkey(m_selct); render(true); break; case KEY_PPAGE: m_selection_changed = true; selection_pageup((int32_t)m_data->size()); update_rowkey(m_selct); render(true); break; case KEY_NPAGE: m_selection_changed = true; selection_pagedown((int32_t)m_data->size()); update_rowkey(m_selct); render(true); break; case KEY_HOME: m_selection_changed = true; selection_home((int32_t)m_data->size()); update_rowkey(m_selct); render(true); break; case KEY_END: m_selection_changed = true; selection_end((int32_t)m_data->size()); update_rowkey(m_selct); render(true); break; case '\n': case '\r': case KEY_ENTER: return STA_DRILLDOWN; case KEY_F(12): return STA_SPECTRO; case 288: return STA_SPECTRO_FILE; case KEY_BACKSPACE: case 127: return STA_DRILLUP; case KEY_MOUSE: { uint32_t j; if(getmouse(&m_last_mevent) == OK) { if(m_last_mevent.bstate & BUTTON1_CLICKED) { // // Bottom menu clicks are handled by the parent // if((uint32_t)m_last_mevent.y == m_parent->m_screenh - 1) { return STA_PARENT_HANDLE; } ASSERT((m_data->size() == 0) || (m_column_startx.size() == m_data->at(0).m_values.size())); if((uint32_t)m_last_mevent.y == m_table_y_start) { // // This is a click on a column header. Change the sorting accordingly. // for(j = 0; j < m_column_startx.size() - 1; j++) { if((uint32_t)m_last_mevent.x >= m_column_startx[j] && (uint32_t)m_last_mevent.x < m_column_startx[j + 1]) { m_table->set_sorting_col(j + 1); break; } } if(j == m_column_startx.size() - 1) { m_table->set_sorting_col(j + 1); } m_table->sort_sample(); update_data(m_data); render(true); } else if((uint32_t)m_last_mevent.y > m_table_y_start && (uint32_t)m_last_mevent.y < m_table_y_start + m_h - 1) { // // This is a click on a row. Update the selection. // m_selection_changed = true; m_selct = m_firstrow + (m_last_mevent.y - m_table_y_start - 1); sanitize_selection((int32_t)m_data->size()); update_rowkey(m_selct); render(true); } } else if(m_last_mevent.bstate & BUTTON1_DOUBLE_CLICKED) { if((uint32_t)m_last_mevent.y > m_table_y_start && (uint32_t)m_last_mevent.y < m_table_y_start + m_h - 1) { // // Update the selection // m_selection_changed = true; m_selct = m_firstrow + (m_last_mevent.y - m_table_y_start - 1); sanitize_selection((int32_t)m_data->size()); update_rowkey(m_selct); render(true); // // This delay is here just as a lazy way to give the user the // feeling that the row has been clicked // usleep(200000); // // Let the ui manager know that a drill down needs to happen // return STA_DRILLDOWN; } } } } break; case 'c': case KEY_DC: if(m_type == sinsp_table::TT_LIST) { m_table->clear(); render(true); m_lastrow_selected = true; } break; default: break; } // // Check if this view has any action configured, and if yes find if this key // is one of the view hotkeys // sinsp_view_info* vinfo = m_parent->get_selected_view(); for(auto hk : vinfo->m_actions) { if(hk.m_hotkey == ch) { m_parent->run_action(&hk); return STA_NONE; } } for(uint32_t i = 0; i < vinfo->max_col_sort_hotkeys; i++) { if(vinfo->m_col_sort_hotkeys[i] == ch) { if(i < vinfo->m_columns.size()) { m_table->set_sorting_col(i + 1); m_table->sort_sample(); update_data(m_data); set_x_start(0); recreate_win(m_parent->m_screenh - 3); render(true); break; } } } return STA_PARENT_HANDLE; } curses_table::alignment curses_table::get_field_alignment(ppm_param_type type) { switch(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_RELTIME: case PT_ABSTIME: case PT_DOUBLE: return ALIGN_RIGHT; default: return ALIGN_LEFT; } } void curses_table::recreate_win(int h) { delwin(m_tblwin); if(h != 0) { m_h = h; } m_tblwin = newwin(m_h, m_w, m_table_y_start, m_table_x_start); render(true); } void curses_table::goto_row(int32_t row) { m_selection_changed = true; selection_goto((int32_t)m_data->size(), row); update_rowkey(row); render(true); } bool curses_table::get_position(OUT int32_t* pos, OUT int32_t* totlines, OUT float* percent, OUT bool* truncated) { if(m_data == NULL || m_data->size() == 0) { return false; } *pos = m_selct + 1; *totlines = (int32_t)m_data->size(); *percent = (float)(m_selct + 1) / (float)m_data->size(); *truncated = false; return true; } void curses_table::follow_end() { if(m_lastrow_selected) { m_selection_changed = true; selection_end((int32_t)m_data->size()); update_rowkey(m_selct); render(true); } } #endif // NOCURSESUI #endif // CSYSDIG sysdig-0.26.4/userspace/libsinsp/cursestable.h000066400000000000000000000046041352731327100214170ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #ifdef CSYSDIG #ifndef NOCURSESUI class curses_table : public curses_scrollable_list, public sinsp_chart { public: enum alignment { ALIGN_LEFT, ALIGN_RIGHT, }; curses_table(sinsp_cursesui* parent, sinsp* inspector, sinsp_table::tabletype type); ~curses_table(); void configure(sinsp_table* table, vector* colsizes, vector* colnames); void update_data(vector* data, bool force_selection_change = false); void render(bool data_changed); sysdig_table_action handle_input(int ch); void set_x_start(uint32_t x) { m_table_x_start = x; } void recreate_win(int h); void update_rowkey(int32_t row); void goto_row(int32_t row); bool get_position(OUT int32_t* pos, OUT int32_t* totlines, OUT float* percent, OUT bool* truncated); void follow_end(); string get_field_val(string fldname); uint32_t get_data_size() { if(m_table != NULL) { return m_data->size(); } else { return 0; } } sinsp_table_field_storage m_last_key; bool m_drilled_up; bool m_selection_changed; MEVENT m_last_mevent; private: alignment get_field_alignment(ppm_param_type type); void print_error(string wstr); void print_wait(); void print_line_centered(string line, int32_t off = 0); sinsp* m_inspector; WINDOW* m_tblwin; sinsp_cursesui* m_parent; sinsp_table* m_table; int32_t m_table_x_start; uint32_t m_table_y_start; uint32_t m_scrolloff_x; uint32_t m_colsizes[PT_MAX]; vector m_legend; vector* m_data; sinsp_filter_check_reference* m_converter; vector m_column_startx; char alignbuf[64]; sinsp_table::tabletype m_type; friend class curses_table_sidemenu; friend class sinsp_cursesui; // for access to m_data in sinsp_cursesui::handle_input }; #endif // NOCURSESUI #endif // CSYSDIG sysdig-0.26.4/userspace/libsinsp/cursesui.cpp000066400000000000000000002106601352731327100213010ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "sinsp.h" #include "sinsp_int.h" #include "../../driver/ppm_ringbuffer.h" #include "filter.h" #include "filterchecks.h" #ifdef CSYSDIG #ifndef _WIN32 #include #else #include #define getch _getch #endif #include "table.h" #include "cursescomponents.h" #include "cursestable.h" #include "cursesspectro.h" #include "ctext.h" #include "cursesui.h" extern int32_t g_csysdig_screen_w; extern bool g_filterchecks_force_raw_times; #ifndef NOCURSESUI #define ColorPair(i,j) COLOR_PAIR((7-i)*8+j) #endif #ifndef _WIN32 static int do_sleep(useconds_t usec) { return usleep(usec); } #else int do_sleep(DWORD usec) { ASSERT(usec >= 1000); Sleep(DWORD(usec / 1000)); return 0; } #endif /////////////////////////////////////////////////////////////////////////////// // json_spy_renderer implementation /////////////////////////////////////////////////////////////////////////////// json_spy_renderer::json_spy_renderer(sinsp* inspector, sinsp_cursesui* parent, int32_t viz_type, spy_text_renderer::sysdig_output_type sotype, bool print_containers, sinsp_evt::param_fmt text_fmt) { m_inspector = inspector; m_filter = NULL; m_root = Json::Value(Json::arrayValue); m_linecnt = 0; m_json_spy_renderer = new spy_text_renderer(inspector, parent, viz_type, sotype, print_containers, text_fmt); } json_spy_renderer::~json_spy_renderer() { delete m_json_spy_renderer; if(m_filter != NULL) { delete m_filter; } } void json_spy_renderer::set_filter(string filter) { if(filter != "") { sinsp_filter_compiler compiler(m_inspector, filter); m_filter = compiler.compile(); } } void json_spy_renderer::process_event_spy(sinsp_evt* evt, int32_t next_res) { int64_t len; const char* argstr = m_json_spy_renderer->process_event_spy(evt, &len); if(argstr != NULL) { Json::Value line; m_linecnt++; uint64_t ts = evt->get_ts(); line["ta"] = to_string(ts); line["td"] = to_string(ts - m_inspector->m_firstevent_ts); ppm_event_flags eflags = evt->get_info_flags(); if(eflags & EF_READS_FROM_FD) { line["d"] = "<"; } else if(eflags & EF_WRITES_TO_FD) { line["d"] = ">"; } line["v"] = argstr; line["l"] = to_string(len); string fdname = evt->get_fd_info()->m_name; string tc; tc.push_back(evt->get_fd_info()->get_typechar()); int64_t fdnum = evt->get_fd_num(); line["fd"] = to_string(fdnum); line["ft"] = string(tc); if(fdname != "") { sanitize_string(fdname); line["f"] = to_string(fdnum) + "(<" + string(tc) + ">" + fdname + ")"; line["fn"] = fdname; } else { line["f"] = to_string(fdnum) + "(<" + string(tc) + ">)"; } sinsp_threadinfo* tinfo = evt->get_thread_info(); ASSERT(tinfo); line["p"] = tinfo->m_comm; if(!tinfo->m_container_id.empty()) { const sinsp_container_info *container_info = m_inspector->m_container_manager.get_container(tinfo->m_container_id); if(container_info) { if(!container_info->m_name.empty()) { line["c"] = container_info->m_name; } } } m_root.append(line); } } void json_spy_renderer::process_event_dig(sinsp_evt* evt, int32_t next_res) { if(!m_inspector->is_debug_enabled() && evt->get_category() & EC_INTERNAL) { return; } string line; m_json_spy_renderer->m_formatter->tostring(evt, &line); m_root.append(line); m_linecnt++; } void json_spy_renderer::process_event(sinsp_evt* evt, int32_t next_res) { // // Filter the event // if(m_filter) { if(!m_filter->run(evt)) { return; } } // // Render the output // if(m_json_spy_renderer->m_viz_type == VIEW_ID_SPY) { process_event_spy(evt, next_res); } else { process_event_dig(evt, next_res); } } string json_spy_renderer::get_data() { Json::FastWriter writer; string res = writer.write(m_root); m_root.clear(); return res; } uint64_t json_spy_renderer::get_count() { return m_linecnt; } /////////////////////////////////////////////////////////////////////////////// // sinsp_cursesui implementation /////////////////////////////////////////////////////////////////////////////// sinsp_cursesui::sinsp_cursesui(sinsp* inspector, string event_source_name, string cmdline_capture_filter, uint64_t refresh_interval_ns, bool print_containers, sinsp_table::output_type output_type, bool is_mousedrag_available, int32_t json_first_row, int32_t json_last_row, int32_t sorting_col, sinsp_evt::param_fmt json_spy_text_fmt) { m_inspector = inspector; m_event_source_name = event_source_name; m_selected_view = 0; m_prev_selected_view = 0; m_selected_view_sidemenu_entry = 0; m_selected_action_sidemenu_entry = 0; m_datatable = NULL; m_cmdline_capture_filter = cmdline_capture_filter; m_paused = false; m_last_input_check_ts = 0; m_output_filtering = false; m_output_searching = false; m_is_filter_sysdig = false; m_eof = 0; m_offline_replay = false; m_last_progress_evt = 0; m_input_check_period_ns = UI_USER_INPUT_CHECK_PERIOD_NS; m_search_nomatch = false; m_chart = NULL; m_n_evts_in_file = 0; m_1st_evt_ts = 0; m_last_evt_ts = 0; m_evt_ts_delta = 0; m_timedelta_formatter = new sinsp_filter_check_reference(); m_refresh_interval_ns = refresh_interval_ns; m_print_containers = print_containers; m_output_type = output_type; m_truncated_input = false; m_view_depth = 0; m_interactive = false; m_json_first_row = json_first_row; m_json_last_row = json_last_row; m_sorting_col = sorting_col; m_json_spy_renderer = NULL; m_json_spy_text_fmt = json_spy_text_fmt; if(output_type == sinsp_table::OT_JSON) { g_filterchecks_force_raw_times = true; } #ifndef NOCURSESUI m_viz = NULL; m_spectro = NULL; m_spybox_text_format = sinsp_evt::PF_NORMAL; m_view_sidemenu = NULL; m_action_sidemenu = NULL; m_spy_box = NULL; m_search_caller_interface = NULL; m_viewinfo_page = NULL; m_mainhelp_page = NULL; m_is_mousedrag_available = is_mousedrag_available; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { init_pair((7-i)*8+j, i, (j==0?-1:j)); } } m_view_sort_sidemenu = NULL; m_selected_view_sort_sidemenu_entry = 0; if(output_type == sinsp_table::OT_CURSES) { // // Colors initialization // m_colors[RESET_COLOR] = ColorPair(COLOR_WHITE,COLOR_BLACK); m_colors[DEFAULT_COLOR] = ColorPair(COLOR_WHITE,COLOR_BLACK); m_colors[FUNCTION_BAR] = ColorPair(COLOR_BLACK,COLOR_YELLOW); m_colors[FUNCTION_KEY] = ColorPair( COLOR_WHITE,COLOR_BLACK); m_colors[PANEL_HEADER_FOCUS] = ColorPair(COLOR_BLACK,COLOR_GREEN); m_colors[PANEL_HEADER_UNFOCUS] = ColorPair(COLOR_BLACK,COLOR_GREEN); m_colors[PANEL_HIGHLIGHT_FOCUS] = ColorPair(COLOR_BLACK,COLOR_CYAN); m_colors[PANEL_HIGHLIGHT_UNFOCUS] = ColorPair(COLOR_BLACK, COLOR_WHITE); m_colors[PANEL_HEADER_LIST_FOCUS] = ColorPair(COLOR_BLACK,COLOR_YELLOW); m_colors[PANEL_HEADER_LIST_HIGHLIGHT] = ColorPair(COLOR_BLACK,COLOR_GREEN); m_colors[FAILED_SEARCH] = ColorPair(COLOR_RED,COLOR_CYAN); m_colors[UPTIME] = A_BOLD | ColorPair(COLOR_CYAN,COLOR_BLACK); m_colors[BATTERY] = A_BOLD | ColorPair(COLOR_CYAN,COLOR_BLACK); m_colors[LARGE_NUMBER] = A_BOLD | ColorPair(COLOR_RED,COLOR_BLACK); m_colors[METER_TEXT] = ColorPair(COLOR_CYAN,COLOR_BLACK); m_colors[METER_VALUE] = A_BOLD | ColorPair(COLOR_CYAN,COLOR_BLACK); m_colors[LED_COLOR] = ColorPair(COLOR_GREEN,COLOR_BLACK); m_colors[TASKS_RUNNING] = A_BOLD | ColorPair(COLOR_GREEN,COLOR_BLACK); m_colors[PROCESS] = A_NORMAL; m_colors[PROCESS_SHADOW] = A_BOLD | ColorPair(COLOR_BLACK,COLOR_BLACK); m_colors[PROCESS_TAG] = A_BOLD | ColorPair(COLOR_YELLOW,COLOR_BLACK); m_colors[PROCESS_MEGABYTES] = ColorPair(COLOR_CYAN,COLOR_BLACK); m_colors[PROCESS_BASENAME] = A_BOLD | ColorPair(COLOR_CYAN,COLOR_BLACK); m_colors[PROCESS_TREE] = ColorPair(COLOR_CYAN,COLOR_BLACK); m_colors[PROCESS_R_STATE] = ColorPair(COLOR_GREEN,COLOR_BLACK); m_colors[PROCESS_D_STATE] = A_BOLD | ColorPair(COLOR_RED,COLOR_BLACK); m_colors[PROCESS_HIGH_PRIORITY] = ColorPair(COLOR_RED,COLOR_BLACK); m_colors[PROCESS_LOW_PRIORITY] = ColorPair(COLOR_RED,COLOR_BLACK); m_colors[PROCESS_THREAD] = ColorPair(COLOR_GREEN,COLOR_BLACK); m_colors[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(COLOR_GREEN,COLOR_BLACK); m_colors[BAR_BORDER] = A_BOLD; m_colors[BAR_SHADOW] = A_BOLD | ColorPair(COLOR_BLACK,COLOR_BLACK); m_colors[SWAP] = ColorPair(COLOR_RED,COLOR_BLACK); m_colors[GRAPH_BLACK] = ColorPair(COLOR_BLACK,COLOR_BLACK); m_colors[GRAPH_WHITE] = ColorPair(COLOR_WHITE,COLOR_WHITE); m_colors[GRAPH_WHITE_D] = ColorPair(COLOR_GREEN,COLOR_WHITE); m_colors[GRAPH_GREEN_L] = ColorPair(COLOR_WHITE,COLOR_GREEN); m_colors[GRAPH_GREEN] = ColorPair(COLOR_WHITE,COLOR_GREEN); m_colors[GRAPH_GREEN_D] = ColorPair(COLOR_YELLOW,COLOR_GREEN); m_colors[GRAPH_YELLOW_L] = ColorPair(COLOR_GREEN,COLOR_YELLOW); m_colors[GRAPH_YELLOW] = ColorPair(COLOR_WHITE,COLOR_YELLOW); m_colors[GRAPH_YELLOW_D] = ColorPair(COLOR_RED,COLOR_YELLOW); m_colors[GRAPH_RED_L] = ColorPair(COLOR_YELLOW,COLOR_RED); m_colors[GRAPH_RED] = ColorPair(COLOR_WHITE,COLOR_RED); m_colors[GRAPH_RED_D] = ColorPair(COLOR_MAGENTA,COLOR_RED); m_colors[GRAPH_MAGENTA_L] = ColorPair(COLOR_RED,COLOR_MAGENTA); m_colors[GRAPH_MAGENTA] = ColorPair(COLOR_MAGENTA,COLOR_MAGENTA); m_colors[MEMORY_USED] = ColorPair(COLOR_GREEN,COLOR_BLACK); m_colors[MEMORY_BUFFERS] = ColorPair(COLOR_BLUE,COLOR_BLACK); m_colors[MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(COLOR_BLUE,COLOR_BLACK); m_colors[MEMORY_CACHE] = ColorPair(COLOR_YELLOW,COLOR_BLACK); m_colors[LOAD_AVERAGE_FIFTEEN] = A_BOLD | ColorPair(COLOR_BLACK,COLOR_BLACK); m_colors[LOAD_AVERAGE_FIVE] = A_NORMAL; m_colors[LOAD_AVERAGE_ONE] = A_BOLD; m_colors[LOAD] = A_BOLD; m_colors[HELP_BOLD] = A_BOLD | ColorPair(COLOR_CYAN,COLOR_BLACK); m_colors[CLOCK] = A_BOLD; m_colors[CHECK_BOX] = ColorPair(COLOR_CYAN,COLOR_BLACK); m_colors[CHECK_MARK] = A_BOLD; m_colors[CHECK_TEXT] = A_NORMAL; m_colors[HOSTNAME] = A_BOLD; m_colors[CPU_NICE] = ColorPair(COLOR_BLUE,COLOR_BLACK); m_colors[CPU_NICE_TEXT] = A_BOLD | ColorPair(COLOR_BLUE,COLOR_BLACK); m_colors[CPU_NORMAL] = ColorPair(COLOR_GREEN,COLOR_BLACK); m_colors[CPU_KERNEL] = ColorPair(COLOR_RED,COLOR_BLACK); m_colors[CPU_IOWAIT] = A_BOLD | ColorPair(COLOR_BLACK, COLOR_BLACK); m_colors[CPU_IRQ] = ColorPair(COLOR_YELLOW,COLOR_BLACK); m_colors[CPU_SOFTIRQ] = ColorPair(COLOR_MAGENTA,COLOR_BLACK); m_colors[SPY_READ] = ColorPair(COLOR_RED,COLOR_BLACK); m_colors[SPY_WRITE] = ColorPair(COLOR_CYAN,COLOR_BLACK); // // Populate the main menu entries // m_menuitems.push_back(sinsp_menuitem_info("F1", "Help", sinsp_menuitem_info::ALL, KEY_F(1))); m_menuitems.push_back(sinsp_menuitem_info("F2", "Views", sinsp_menuitem_info::ALL, KEY_F(2))); m_menuitems.push_back(sinsp_menuitem_info("F4", "Filter", sinsp_menuitem_info::ALL, KEY_F(4))); m_menuitems.push_back(sinsp_menuitem_info("F5", "Echo", sinsp_menuitem_info::TABLE, KEY_F(5))); m_menuitems.push_back(sinsp_menuitem_info("F6", "Dig", sinsp_menuitem_info::TABLE, KEY_F(6))); m_menuitems.push_back(sinsp_menuitem_info("F7", "Legend", sinsp_menuitem_info::ALL, KEY_F(7))); m_menuitems.push_back(sinsp_menuitem_info("F8", "Actions", sinsp_menuitem_info::ALL, KEY_F(8))); m_menuitems.push_back(sinsp_menuitem_info("F9", "Sort", sinsp_menuitem_info::ALL, KEY_F(9))); m_menuitems.push_back(sinsp_menuitem_info("F12", "Spectro", sinsp_menuitem_info::ALL, KEY_F(12))); m_menuitems.push_back(sinsp_menuitem_info("CTRL+F", "Search", sinsp_menuitem_info::ALL, 6)); m_menuitems.push_back(sinsp_menuitem_info("p", "Pause", sinsp_menuitem_info::ALL, 'p')); m_menuitems.push_back(sinsp_menuitem_info("c", "Clear", sinsp_menuitem_info::LIST, 'c')); m_menuitems_spybox.push_back(sinsp_menuitem_info("F1", "Help", sinsp_menuitem_info::ALL, KEY_F(1))); m_menuitems_spybox.push_back(sinsp_menuitem_info("F2", "View As", sinsp_menuitem_info::ALL, KEY_F(2))); m_menuitems_spybox.push_back(sinsp_menuitem_info("CTRL+F", "Search", sinsp_menuitem_info::ALL, 6)); m_menuitems_spybox.push_back(sinsp_menuitem_info("p", "Pause", sinsp_menuitem_info::ALL, 'p')); m_menuitems_spybox.push_back(sinsp_menuitem_info("Bak", "Back", sinsp_menuitem_info::ALL, KEY_BACKSPACE)); m_menuitems_spybox.push_back(sinsp_menuitem_info("c", "Clear", sinsp_menuitem_info::ALL, 'c')); m_menuitems_spybox.push_back(sinsp_menuitem_info("CTRL+G", "Goto", sinsp_menuitem_info::ALL, 7)); // // Get screen dimensions // getmaxyx(stdscr, m_screenh, m_screenw); g_csysdig_screen_w = m_screenw; } #endif } sinsp_cursesui::~sinsp_cursesui() { if(m_datatable != NULL) { delete m_datatable; } if(m_json_spy_renderer != NULL) { delete m_json_spy_renderer; } #ifndef NOCURSESUI if(m_output_type == sinsp_table::OT_CURSES) { if(m_viz != NULL) { delete m_viz; } if(m_spectro != NULL) { delete m_spectro; } if(m_view_sidemenu != NULL) { delete m_view_sidemenu; } if(m_action_sidemenu != NULL) { delete m_action_sidemenu; } if(m_viewinfo_page != NULL) { delete m_viewinfo_page; } if(m_mainhelp_page != NULL) { delete m_mainhelp_page; } if(m_spy_box) { delete m_spy_box; } } #endif delete m_timedelta_formatter; } void sinsp_cursesui::configure(sinsp_view_manager* views) { if(views == NULL) { ASSERT(false); throw sinsp_exception("trying to configure the command line UI with no views"); } // // Copy the input views // m_views = *views; // // Determine which view is the starting one // m_selected_view = m_views.get_selected_view(); m_selected_view_sidemenu_entry = m_selected_view; m_selected_action_sidemenu_entry = 0; m_selected_view_sort_sidemenu_entry = 0; m_sidemenu_sorting_col = -1; } void sinsp_cursesui::start(bool is_drilldown, bool is_spy_switch) { // // Input validation // if(m_selected_view >= 0) { if(m_selected_view >= (int32_t)m_views.size()) { if(m_views.size() == 0) { throw sinsp_exception("no views loaded"); } else { ASSERT(false); throw sinsp_exception("invalid view"); } } } // // Delete the previous table and visualizations // if(m_datatable != NULL) { delete m_datatable; m_datatable = NULL; } if(m_json_spy_renderer != NULL) { delete m_json_spy_renderer; m_json_spy_renderer = NULL; } #ifndef NOCURSESUI spy_text_renderer::sysdig_output_type dig_otype = spy_text_renderer::OT_NORMAL; if(m_output_type == sinsp_table::OT_CURSES) { if(m_viz != NULL) { delete m_viz; m_viz = NULL; } if(m_spectro != NULL) { delete m_spectro; m_spectro = NULL; if(m_views.at(m_prev_selected_view)->m_drilldown_target == "dig_app") { dig_otype = spy_text_renderer::OT_LATENCY_APP; } else { dig_otype = spy_text_renderer::OT_LATENCY; } } if(m_spy_box && !is_spy_switch) { delete m_spy_box; m_spy_box = NULL; } m_chart = NULL; } #endif // // Update the filter based on what's selected // create_complete_filter(false); // // If we need a new datatable, allocate it and set it up // sinsp_view_info* wi = NULL; sinsp_table::tabletype ty = sinsp_table::TT_NONE; if(m_selected_view >= 0) { wi = m_views.at(m_selected_view); if(wi->m_type == sinsp_view_info::T_TABLE) { ty = sinsp_table::TT_TABLE; m_datatable = new sinsp_table(m_inspector, ty, m_refresh_interval_ns, m_output_type, m_json_first_row, m_json_last_row); } else if(wi->m_type == sinsp_view_info::T_LIST) { ty = sinsp_table::TT_LIST; m_datatable = new sinsp_table(m_inspector, ty, m_refresh_interval_ns, m_output_type, m_json_first_row, m_json_last_row); } else if(wi->m_type == sinsp_view_info::T_SPECTRO) { ty = sinsp_table::TT_TABLE; // // Accelerate the refresh rate to 1/2s // if(m_refresh_interval_ns == 2000000000) { m_datatable = new sinsp_table(m_inspector, ty, m_refresh_interval_ns / 4, m_output_type, m_json_first_row, m_json_last_row); } else { m_datatable = new sinsp_table(m_inspector, ty, m_refresh_interval_ns, m_output_type, m_json_first_row, m_json_last_row); } } else { ASSERT(false); } try { m_datatable->configure(&wi->m_columns, m_complete_filter, wi->m_use_defaults, m_view_depth); } catch(...) { delete m_datatable; m_datatable = NULL; throw; } if(m_sorting_col != -1 && m_sorting_col < (int32_t)wi->m_columns.size()) { m_datatable->set_sorting_col(m_sorting_col); } else { m_datatable->set_sorting_col(wi->m_sortingcol); } } else { // // Create the visualization component // if(m_output_type == sinsp_table::OT_JSON) { m_json_spy_renderer= new json_spy_renderer(m_inspector, this, m_selected_view, spy_text_renderer::OT_NORMAL, m_print_containers, m_json_spy_text_fmt); m_json_spy_renderer->set_filter(m_complete_filter); } #ifndef NOCURSESUI else { m_spy_box = new curses_textbox(m_inspector, this, m_selected_view, dig_otype); m_spy_box->reset(); m_chart = m_spy_box; m_spy_box->set_filter(m_complete_filter); } #endif } #ifndef NOCURSESUI if(m_output_type != sinsp_table::OT_CURSES) { return; } // // If we need a table or spectrogram visualization, allocate it and set it up // if(m_output_type != sinsp_table::OT_JSON) { if(m_selected_view >= 0) { if(wi != NULL && wi->m_type == sinsp_view_info::T_SPECTRO) { ASSERT(ty == sinsp_table::TT_TABLE); m_spectro = new curses_spectro(this, m_inspector, m_views.at(m_selected_view)->m_id == "spectro_traces"); m_viz = NULL; m_chart = m_spectro; } else { ASSERT(ty != sinsp_table::TT_NONE); m_viz = new curses_table(this, m_inspector, ty); m_spectro = NULL; m_chart = m_viz; } vector colsizes; vector colnames; ASSERT(wi != NULL); wi->get_col_names_and_sizes(&colnames, &colsizes); if(m_viz) { ASSERT(m_spectro == NULL); m_viz->configure(m_datatable, &colsizes, &colnames); } else { ASSERT(m_spectro != NULL); m_spectro->configure(m_datatable); } if(!is_drilldown) { populate_view_sidemenu("", &m_sidemenu_viewlist); } } } #endif m_prev_selected_view = m_selected_view; } #ifndef NOCURSESUI void sinsp_cursesui::render_header() { uint32_t j = 0; uint32_t k = 0; // // Show the 'viewing' line // attrset(m_colors[HELP_BOLD]); move(0, 0); for(j = 0; j < m_screenw; j++) { addch(' '); } mvaddstr(0, 0, "Viewing:"); k += sizeof("Viewing: ") - 1; attrset(m_colors[sinsp_cursesui::PROCESS]); string vs; if(m_selected_view >= 0) { sinsp_view_info* sv = get_selected_view(); const char* vcs = sv->m_name.c_str(); vs = vcs; } else { if(m_selected_view == VIEW_ID_SPY) { vs = "I/O activity"; } else if(m_selected_view == VIEW_ID_DIG) { vs = "sysdig output"; } else { ASSERT(false); } } mvaddstr(0, k, vs.c_str()); k+= vs.size() + 1; attrset(m_colors[HELP_BOLD]); mvaddstr(0, k, "For: "); k += sizeof("For: ") - 1; attrset(m_colors[sinsp_cursesui::PROCESS]); if(m_sel_hierarchy.size() != 0) { vs = ""; for(j = 0; j < m_sel_hierarchy.size(); j++) { uint32_t pv = m_sel_hierarchy.at(j)->m_prev_selected_view; if(m_sel_hierarchy.at(j)->m_field == "") { continue; } if(m_views.at(pv)->m_type == sinsp_view_info::T_SPECTRO) { //vs += m_sel_hierarchy.at(j)->m_prev_manual_filter.c_str(); vs += "spectrogram area"; } else { vs += m_sel_hierarchy.at(j)->m_field; vs += "="; vs += m_sel_hierarchy.at(j)->m_val; } if(j < m_sel_hierarchy.size() - 1) { vs += " and "; } } if(vs == "") { vs = "whole machine"; } } else { vs = "whole machine"; } mvaddstr(0, k, vs.c_str()); if(m_paused) { string wstr = "PAUSED"; attrset(m_colors[sinsp_cursesui::LARGE_NUMBER]); mvprintw(0, m_screenw / 2 - wstr.size() / 2, wstr.c_str()); } // // Show the 'filter' line // attrset(m_colors[HELP_BOLD]); move(1, 0); for(uint32_t j = 0; j < m_screenw; j++) { addch(' '); } attrset(m_colors[HELP_BOLD]); mvaddstr(1, 0, "Source:"); k = sizeof("Source: ") - 1; attrset(m_colors[sinsp_cursesui::PROCESS]); string srcstr; if(m_inspector->is_live()) { srcstr = "Live System"; } else { if(m_n_evts_in_file == 0) { m_n_evts_in_file = m_inspector->get_num_events(); m_evt_ts_delta = m_last_evt_ts - m_1st_evt_ts; } srcstr = m_inspector->get_input_filename(); srcstr += " (" + to_string(m_n_evts_in_file) + " evts, "; if(m_truncated_input) { srcstr += " truncated, "; } m_timedelta_formatter->set_val(PT_RELTIME, (uint8_t*)&m_evt_ts_delta, 8, 0, ppm_print_format::PF_DEC); srcstr += string(m_timedelta_formatter->tostring_nice(NULL, 0, 0)) + ")"; } mvaddnstr(1, k, srcstr.c_str(), m_screenw - k - 1); k += srcstr.size() + 1; m_filterstring_start_x = k; attrset(m_colors[HELP_BOLD]); mvaddstr(1, k, "Filter:"); k += sizeof("Filter: ") - 1; attrset(m_colors[sinsp_cursesui::PROCESS]); string sflt; if(m_complete_filter != "") { sflt = m_complete_filter.c_str(); } else { sflt = "none"; } mvaddnstr(1, k, sflt.c_str(), m_screenw - k - 1); k += sflt.size(); m_filterstring_end_x = k; } void sinsp_cursesui::turn_search_on(search_caller_interface* ifc, string header_text) { ASSERT(m_spy_box != NULL); m_search_header_text = header_text; m_spy_box->get_offset(&m_search_start_x, &m_search_start_y); m_search_caller_interface = ifc; m_output_searching = false; m_output_filtering = false; m_cursor_pos = 0; curs_set(1); render(); } void sinsp_cursesui::draw_bottom_menu(vector* items, bool istable) { uint32_t j = 0; uint32_t k = 0; // // Clear the line // move(m_screenh - 1, 0); for(uint32_t j = 0; j < m_screenw; j++) { addch(' '); } m_mouse_to_key_list.clear(); for(j = 0; j < items->size(); j++) { if(istable && ((items->at(j).m_type & sinsp_menuitem_info::TABLE) == 0)) { continue; } if((!istable) && ((items->at(j).m_type & sinsp_menuitem_info::LIST) == 0)) { continue; } uint32_t startx = k; attrset(m_colors[PROCESS]); string fks = items->at(j).m_key; mvaddnstr(m_screenh - 1, k, fks.c_str(), MAX(fks.size(), 2)); k += MAX(fks.size(), 2); attrset(m_colors[PANEL_HIGHLIGHT_FOCUS]); fks = items->at(j).m_desc; if(fks.size() < 6) { fks.resize(6, ' '); } mvaddnstr(m_screenh - 1, k, fks.c_str(), fks.size()); k += fks.size(); m_mouse_to_key_list.add(sinsp_mouse_to_key_list_entry(startx, m_screenh - 1, k - 1, m_screenh - 1, items->at(j).m_keyboard_equivalent)); } } void sinsp_cursesui::render_default_main_menu() { bool istable; if(m_datatable != NULL && m_datatable->m_type == sinsp_table::TT_TABLE) { istable = true; } else { istable = false; } draw_bottom_menu(&m_menuitems, istable); } void sinsp_cursesui::render_spy_main_menu() { draw_bottom_menu(&m_menuitems_spybox, false); } void sinsp_cursesui::render_filtersearch_main_menu() { uint32_t k = 0; string* str = 0; // // Pick the right string based on what we're doing // if(m_output_filtering) { str = &m_manual_filter; if(*str == "" && m_is_filter_sysdig && m_complete_filter != "") { *str = m_complete_filter; } } else if(m_output_searching) { str = &m_manual_search_text; } else { if(m_search_caller_interface) { str = m_search_caller_interface->get_last_search_string(); } else { ASSERT(false); } } // // Only clear the line if this is the first refresh, to prevent deleting the // text that the user is typing // if(m_cursor_pos == 0) { move(m_screenh - 1, 0); for(uint32_t j = 0; j < m_screenw; j++) { addch(' '); } } attrset(m_colors[PROCESS]); string fks = "F1"; mvaddnstr(m_screenh - 1, k, fks.c_str(), 10); k += fks.size(); attrset(m_colors[PANEL_HIGHLIGHT_FOCUS]); fks = "Help"; fks.resize(6, ' '); mvaddnstr(m_screenh - 1, k, fks.c_str(), 6); k += 6; if(m_output_filtering) { attrset(m_colors[PROCESS]); fks = "F2"; mvaddnstr(m_screenh - 1, k, fks.c_str(), 10); k += fks.size(); attrset(m_colors[PANEL_HIGHLIGHT_FOCUS]); if(m_is_filter_sysdig) { fks = "Text"; } else { fks = "sysdig"; } fks.resize(6, ' '); mvaddnstr(m_screenh - 1, k, fks.c_str(), 6); k += 6; } attrset(m_colors[PROCESS]); fks = "Enter"; mvaddnstr(m_screenh - 1, k, fks.c_str(), 10); k += fks.size(); attrset(m_colors[PANEL_HIGHLIGHT_FOCUS]); fks = "Done"; fks.resize(6, ' '); mvaddnstr(m_screenh - 1, k, fks.c_str(), 6); k += 6; attrset(m_colors[PROCESS]); fks = "Esc"; mvaddnstr(m_screenh - 1, k, fks.c_str(), 10); k += fks.size(); attrset(m_colors[PANEL_HIGHLIGHT_FOCUS]); fks = "Clear"; fks.resize(6, ' '); mvaddnstr(m_screenh - 1, k, fks.c_str(), 6); k += 6; k++; attrset(m_colors[PANEL_HIGHLIGHT_FOCUS]); if(m_is_filter_sysdig) { fks = "Expression: "; } else { if(m_search_header_text == "") { fks = "Text to match: "; } else { fks = m_search_header_text + ": "; } } mvaddnstr(m_screenh - 1, k, fks.c_str(), 20); k += fks.size(); uint32_t cursor_pos = k; if(m_cursor_pos == 0) { for(; k < m_screenw; k++) { addch(' '); } m_cursor_pos = cursor_pos; mvprintw(m_screenh - 1, m_cursor_pos, str->c_str()); m_cursor_pos += str->size(); } move(m_screenh - 1, m_cursor_pos); } void sinsp_cursesui::render_position_info() { if(m_chart == NULL) { return; } int32_t pos; int32_t totlines; float percent; bool truncated; if(m_chart->get_position(&pos, &totlines, &percent, &truncated)) { char prstr[128]; string trs; uint32_t csize = 18; attrset(m_colors[sinsp_cursesui::PROCESS_MEGABYTES]); move(m_screenh - 1, m_screenw - csize); for(uint32_t k = 0; k < csize; k++) { addch(' '); } if(truncated) { trs = "(truncated)"; } if(percent != 0) { sprintf(prstr, "%d/%d(%.1f%%)%s", (int)pos, (int)totlines, percent * 100, trs.c_str()); } else { sprintf(prstr, "%d/%d(0.0%%)%s", (int)pos, (int)totlines, trs.c_str()); } mvaddstr(m_screenh - 1, m_screenw - strlen(prstr), prstr); } } void sinsp_cursesui::render_main_menu() { if(m_output_filtering || m_output_searching || m_search_caller_interface != NULL) { render_filtersearch_main_menu(); } else if(m_spy_box != NULL) { render_spy_main_menu(); } else { render_default_main_menu(); } } void sinsp_cursesui::render() { if(m_spectro && !m_view_sidemenu) { return; } // // Draw the header at the top of the page // render_header(); // // Print the position in the chart // if(m_output_filtering || m_output_searching || m_search_caller_interface != NULL) { render_position_info(); } // // Draw the menu at the bottom of the screen // render_main_menu(); // // If required, draw the side menu // if(m_view_sidemenu) { m_view_sidemenu->render(); } if(m_view_sort_sidemenu) { m_view_sort_sidemenu->render(); } if(m_action_sidemenu) { m_action_sidemenu->render(); } // // Print the position in the chart // if(!(m_output_filtering || m_output_searching || m_search_caller_interface != NULL)) { render_position_info(); } } #endif sinsp_view_info* sinsp_cursesui::get_selected_view() { if(m_selected_view < 0) { return NULL; } ASSERT(m_selected_view < (int32_t)m_views.size()); return m_views.at(m_selected_view); } sinsp_view_info* sinsp_cursesui::get_prev_selected_view() { if(m_prev_selected_view < 0) { return NULL; } ASSERT(m_prev_selected_view < (int32_t)m_views.size()); return m_views.at(m_prev_selected_view); } #ifndef NOCURSESUI void sinsp_cursesui::populate_view_sidemenu(string field, vector* viewlist) { uint32_t k = 0; viewlist->clear(); uint64_t bpos = field.find('['); if(bpos != string::npos) { field = field.substr(0, bpos); } for(uint32_t j = 0; j < m_views.size(); ++j) { auto it = m_views.at(j); for(auto atit = it->m_applies_to.begin(); atit != it->m_applies_to.end(); ++atit) { if(*atit == field) { viewlist->push_back(sidemenu_list_entry(it->m_name, j)); if(it->m_name == m_views.at(m_selected_view)->m_name) { m_selected_view_sidemenu_entry = k; if(m_view_sidemenu != NULL) { m_view_sidemenu->m_selct = k; } } k++; } } } if(m_view_sidemenu != NULL) { m_view_sidemenu->set_entries(viewlist); } } void sinsp_cursesui::populate_view_cols_sidemenu() { int32_t k = 0; vector viewlist; sinsp_view_info* vinfo = get_selected_view(); for(auto it : vinfo->m_columns) { if(it.m_name != "NA") { if(m_sidemenu_sorting_col == k) { viewlist.push_back(sidemenu_list_entry(it.m_name, k++)); continue; } viewlist.push_back(sidemenu_list_entry(it.m_name, k++)); } } if(viewlist.size() == 0) { viewlist.push_back(sidemenu_list_entry("", 0)); } if(m_view_sort_sidemenu != NULL) { m_view_sort_sidemenu->set_entries(&viewlist); } } void sinsp_cursesui::populate_action_sidemenu() { uint32_t k = 0; vector viewlist; m_selected_action_sidemenu_entry = 0; sinsp_view_info* vinfo = get_selected_view(); for(auto hk : vinfo->m_actions) { string str = string("(") + hk.m_hotkey + ") " + hk.m_description; viewlist.push_back(sidemenu_list_entry(str, k++)); } if(viewlist.size() == 0) { viewlist.push_back(sidemenu_list_entry("", 0)); } if(m_action_sidemenu != NULL) { m_action_sidemenu->m_selct = 0; m_action_sidemenu->set_entries(&viewlist); } } #endif // NOCURSESUI string combine_filters(string flt1, string flt2) { if(flt1 == "") { return flt2; } else { if(flt2 == "") { return flt1; } } string res = "(" + flt1 + ") and (" + flt2 + ")"; return res; } Json::Value sinsp_cursesui::generate_json_info_section() { Json::Value jinfo; Json::Value jlegend; sinsp_view_info* wi = NULL; if(m_selected_view >= 0) { wi = m_views.at(m_selected_view); vector colsizes; vector colnames; ASSERT(wi != NULL); jinfo["sortingCol"] = wi->m_sortingcol; for(auto av : wi->m_applies_to) { jinfo["appliesTo"].append(av); } sinsp_view_column_info* kinfo = wi->get_key(); if(kinfo) { jinfo["drillDownKeyField"] = kinfo->m_field; jinfo["canDrillDown"] = true; } else { jinfo["canDrillDown"] = false; } wi->get_col_names_and_sizes(&colnames, &colsizes); uint32_t off; if(colnames.size() == m_datatable->m_types->size() - 1) { off = 1; } else { off = 0; } vector* tlegend = m_datatable->get_legend(); ASSERT(tlegend->size() == colnames.size()); for(uint32_t j = 1; j < colnames.size(); j++) { Json::Value jcinfo; jcinfo["name"] = colnames[j]; jcinfo["size"] = colsizes[j]; jcinfo["type"] = param_type_to_string(m_datatable->m_types->at(j + off)); jcinfo["format"] = print_format_to_string(tlegend->at(j).m_print_format); jlegend.append(jcinfo); } } jinfo["legend"] = jlegend; return jinfo; } void sinsp_cursesui::handle_end_of_sample(sinsp_evt* evt, int32_t next_res) { vector* sample; m_datatable->flush(evt); // // It's time to refresh the data for this chart. // First of all, create the data for the chart // if(m_output_type == sinsp_table::OT_JSON && (m_inspector->is_live() || (m_eof > 0))) { printf("{\"progress\": 100, "); sample = m_datatable->get_sample(get_time_delta()); printf("\"count\": %" PRIu64 ", ", m_datatable->m_json_output_lines_count); Json::Value root = generate_json_info_section(); if(m_views.at(m_selected_view)->m_type == sinsp_view_info::T_TABLE) { bool res; execute_table_action(STA_DRILLDOWN_TEMPLATE, 0, &res); create_complete_filter(true); root["filterTemplateF"] = m_complete_filter; root["filterTemplate"] = m_complete_filter_noview; } Json::FastWriter writer; string jstr = writer.write(root); printf("\"info\": %s", jstr.substr(0, jstr.size() - 1).c_str()); printf("}\n"); //printf("%c", EOF); } else { if(m_output_type != sinsp_table::OT_JSON) { sample = m_datatable->get_sample(get_time_delta()); } } #ifndef NOCURSESUI if(m_output_type == sinsp_table::OT_CURSES) { // // If the help page has been shown, don't update the screen // if(m_viewinfo_page != NULL || m_mainhelp_page != NULL) { return; } // // Now refresh the UI. // if(!m_paused) { if(m_viz) { ASSERT(m_spectro == NULL); m_viz->update_data(sample); if(m_datatable->m_type == sinsp_table::TT_LIST && m_inspector->is_live()) { m_viz->follow_end(); } m_viz->render(true); } else if(m_spectro) { ASSERT(m_viz == NULL); m_spectro->update_data(sample); m_spectro->render(true); } } render(); } #endif // // If this is a trace file, check if we reached the end of the file. // Or, if we are in replay mode, wait for a key press before processing // the next sample. // if(!m_inspector->is_live()) { #ifndef NOCURSESUI /* if(m_output_type == sinsp_table::OT_CURSES) { if(m_offline_replay) { while(getch() != ' ') { usleep(10000); } } } */ #endif } } void sinsp_cursesui::restart_capture(bool is_spy_switch) { if(!m_inspector->is_live() && m_n_evts_in_file == 0) { m_n_evts_in_file = m_inspector->get_num_events(); m_evt_ts_delta = m_last_evt_ts - m_1st_evt_ts; } m_inspector->close(); start(true, is_spy_switch); m_inspector->open(m_event_source_name); } void sinsp_cursesui::create_complete_filter(bool templated) { if(m_is_filter_sysdig) { if(m_manual_filter != "") { m_complete_filter = m_manual_filter; } m_complete_filter_noview = m_complete_filter; } else { m_complete_filter = m_cmdline_capture_filter; m_complete_filter = combine_filters(m_complete_filter, m_sel_hierarchy.tofilter(templated)); m_complete_filter_noview = m_complete_filter; // // Note: m_selected_view is smaller than 0 when there's no view, because we're doing // non-view stuff like spying. // if(m_selected_view >= 0) { m_complete_filter = combine_filters(m_complete_filter, m_views.at(m_selected_view)->get_filter(m_view_depth)); } } } void sinsp_cursesui::switch_view(bool is_spy_switch) { #ifndef NOCURSESUI if(m_output_type == sinsp_table::OT_CURSES) { // // Clear the screen to make sure all the crap is removed // clear(); // // If we're currently visualizing the spy box, reset it and return immediately // if(is_spy_switch) { if(m_spy_box) { m_spy_box->reset(); } } } #endif // // Put the current view in the hierarchy stack // #if 1 sinsp_view_info* psv = get_prev_selected_view(); if(psv != NULL) { if(m_sel_hierarchy.size() > 0) { sinsp_ui_selection_info* psinfo = m_sel_hierarchy.at(m_sel_hierarchy.size() - 1); m_sel_hierarchy.push_back(psinfo->m_field, psinfo->m_val, psv->get_key(), psinfo->m_view_filter, m_prev_selected_view, m_selected_view_sidemenu_entry, NULL, psv->m_sortingcol, m_manual_filter, m_is_filter_sysdig, m_datatable->is_sorting_ascending(), false); } else { m_sel_hierarchy.push_back("", "", psv->get_key(), "", m_prev_selected_view, m_selected_view_sidemenu_entry, NULL, psv->m_sortingcol, m_manual_filter, m_is_filter_sysdig, m_datatable->is_sorting_ascending(), false); } } #endif // // Clear the manual filter, but not if this is a sysdig filter and we're in the same // view (applying sysdig filters causes the same view to the reloaded, and in that // case we want to preserve the filter). // if(m_prev_selected_view != m_selected_view) { m_manual_filter = ""; } // // If this is a file, we need to restart the capture. // If it's a live capture, we restart only if start() fails, which usually // happens in case one of the filter fields requested thread state. // if(!m_inspector->is_live()) { m_eof = 0; m_last_progress_evt = 0; restart_capture(is_spy_switch); } else { // // When live, also make sure to unpause the viz, otherwise the screen // will stay empty. // if(m_paused) { pause(); } try { start(true, is_spy_switch); } catch(...) { restart_capture(is_spy_switch); } } #ifndef NOCURSESUI if(m_output_type == sinsp_table::OT_CURSES) { delete m_view_sidemenu; m_view_sidemenu = NULL; delete m_action_sidemenu; m_action_sidemenu = NULL; delete m_view_sort_sidemenu; m_view_sort_sidemenu = NULL; if(m_viz != NULL) { m_viz->render(true); } else if(m_spectro != NULL) { m_spectro->render(true); } render(); } #endif } void sinsp_cursesui::spy_selection(string field, string val, sinsp_view_column_info* column_info, bool is_dig) { uint32_t srtcol; sinsp_table_field rowkeybak; #ifdef NOCURSESUI if(true) #else if(m_viz) #endif { #ifndef NOCURSESUI sinsp_table_field* rowkey = m_datatable->get_row_key(m_viz->m_selct); #else sinsp_table_field* rowkey = NULL; #endif if(rowkey != NULL) { rowkeybak.m_val = new uint8_t[rowkey->m_len]; memcpy(rowkeybak.m_val, rowkey->m_val, rowkey->m_len); rowkeybak.m_len = rowkey->m_len; } srtcol = m_datatable->get_sorting_col(); } #ifndef NOCURSESUI else if(m_spectro) { m_is_filter_sysdig = true; m_manual_filter = m_spectro->m_selection_filter; srtcol = 0; rowkeybak.m_val = NULL; rowkeybak.m_len = 0; srtcol = 2; } else { ASSERT(false); return; } #endif ASSERT(m_selected_view < (int32_t)m_views.size()); if(m_views.at(m_selected_view)->m_drilldown_increase_depth) { m_view_depth++; } string vfilter; if(m_views.at(m_selected_view)->m_propagate_filter) { vfilter = m_views.at(m_selected_view)->get_filter(m_view_depth); } else { vfilter = ""; } m_sel_hierarchy.push_back(field, val, column_info, vfilter, m_selected_view, m_selected_view_sidemenu_entry, &rowkeybak, srtcol, m_manual_filter, m_is_filter_sysdig, m_datatable->is_sorting_ascending(), true); if(is_dig) { m_selected_view = VIEW_ID_DIG; } else { m_selected_view = VIEW_ID_SPY; } if(!m_inspector->is_live()) { m_eof = 0; m_last_progress_evt = 0; restart_capture(false); } else { try { start(true, false); } catch(...) { restart_capture(false); } } #ifndef NOCURSESUI render(); #endif } // returns false if there is no suitable drill down view for this field bool sinsp_cursesui::do_drilldown(string field, string val, sinsp_view_column_info* column_info, uint32_t new_view_num, filtercheck_field_info* info, bool dont_restart) { // // unpause the thing if it's paused // if(m_paused) { pause(); } // // escape string parameters // if(info != NULL && info->m_type & PT_CHARBUF) { string escape = "\""; val = escape + val + escape; } // // Do the drilldown // sinsp_table_field* rowkey = NULL; #ifndef NOCURSESUI if(m_viz != NULL) { rowkey = m_datatable->get_row_key(m_viz->m_selct); } #endif sinsp_table_field rowkeybak; if(rowkey != NULL) { rowkeybak.m_val = new uint8_t[rowkey->m_len]; memcpy(rowkeybak.m_val, rowkey->m_val, rowkey->m_len); rowkeybak.m_len = rowkey->m_len; } uint32_t srtcol; srtcol = m_datatable->get_sorting_col(); if(m_views.at(m_selected_view)->m_drilldown_increase_depth) { if(m_views.at(new_view_num)->m_id != "spectro_tracers") { m_view_depth++; } } string vfilter; if(m_views.at(m_selected_view)->m_propagate_filter) { vfilter = m_views.at(m_selected_view)->get_filter(m_view_depth); } else { vfilter = ""; } m_sel_hierarchy.push_back(field, val, column_info, vfilter, m_selected_view, m_selected_view_sidemenu_entry, &rowkeybak, srtcol, m_manual_filter, m_is_filter_sysdig, m_datatable->is_sorting_ascending(), true); m_selected_view = new_view_num; // // Reset the filter // #ifndef NOCURSESUI if(m_output_type != sinsp_table::OT_JSON) { if(m_viz != NULL) { m_manual_filter = ""; m_is_filter_sysdig = false; } else { ASSERT(m_spectro != NULL); m_is_filter_sysdig = true; m_manual_filter = m_spectro->m_selection_filter; } } #endif if(!dont_restart) { if(!m_inspector->is_live()) { m_eof = 0; m_last_progress_evt = 0; restart_capture(false); } else { try { start(true, false); } catch(...) { restart_capture(false); } } #ifndef NOCURSESUI clear(); populate_view_sidemenu(field, &m_sidemenu_viewlist); populate_action_sidemenu(); if(m_viz) { m_viz->render(true); } else if(m_spectro) { m_spectro->render(true); } render(); #endif } return true; } // returns false if there is no suitable drill down view for this field bool sinsp_cursesui::drilldown(string field, string val, sinsp_view_column_info* column_info, filtercheck_field_info* info, bool dont_restart) { uint32_t j = 0; for(j = 0; j < m_views.size(); ++j) { if(m_views.at(j)->m_id == m_views.at(m_selected_view)->m_drilldown_target) { return do_drilldown(field, val, column_info, j, info, dont_restart); } } for(j = 0; j < m_views.size(); ++j) { auto it = m_views.at(j); for(auto atit = it->m_applies_to.begin(); atit != it->m_applies_to.end(); ++atit) { if(*atit == field) { return do_drilldown(field, val, column_info, j, info, dont_restart); } } } return false; } bool sinsp_cursesui::spectro_selection(string field, string val, sinsp_view_column_info* column_info, filtercheck_field_info* info, sysdig_table_action ta) { uint32_t j = 0; string spectro_name; if(m_views.at(m_selected_view)->m_spectro_type == "tracers") { spectro_name = "spectro_traces"; } else { if(ta == STA_SPECTRO) { spectro_name = "spectro_all"; } else { spectro_name = "spectro_file"; } } for(j = 0; j < m_views.size(); ++j) { if(m_views.at(j)->m_id == spectro_name) { return do_drilldown(field, val, column_info, j, info, false); } } return false; } bool sinsp_cursesui::drillup() { if(m_sel_hierarchy.size() > 0) { // // unpause the thing if it's paused // if(m_paused) { pause(); } // // Do the drillup // string field; sinsp_ui_selection_info* psinfo = NULL; sinsp_ui_selection_info* sinfo = m_sel_hierarchy.at(m_sel_hierarchy.size() - 1); bool is_spctro_app = false; if(m_selected_view > 0 && m_views.at(m_selected_view)->m_id == "spectro_tracers") { is_spctro_app = true; } m_manual_filter = ""; if(m_sel_hierarchy.size() > 1) { psinfo = m_sel_hierarchy.at(m_sel_hierarchy.size() - 2); field = psinfo->m_field; } sinsp_table_field rowkey = sinfo->m_rowkey; m_selected_view = sinfo->m_prev_selected_view; m_selected_view_sidemenu_entry = sinfo->m_prev_selected_sidemenu_entry; if(m_views.at(m_selected_view)->m_drilldown_increase_depth && !is_spctro_app) { if(sinfo != NULL && sinfo->m_is_drilldown) { m_view_depth--; } } if(m_views.at(m_selected_view)->m_type == sinsp_view_info::T_SPECTRO) { m_is_filter_sysdig = false; } else { m_manual_filter = sinfo->m_prev_manual_filter; m_is_filter_sysdig = sinfo->m_prev_is_filter_sysdig; } bool is_sorting_ascending = sinfo->m_prev_is_sorting_ascending; ASSERT(m_selected_view < (int32_t)m_views.size()); m_sel_hierarchy.pop_back(); //m_views[m_selected_view].m_filter = m_sel_hierarchy.tofilter(); m_complete_filter = m_cmdline_capture_filter; m_complete_filter = combine_filters(m_complete_filter, m_sel_hierarchy.tofilter(false)); if(!m_inspector->is_live()) { m_eof = 0; m_last_progress_evt = 0; restart_capture(false); } else { try { start(true, false); } catch(...) { restart_capture(false); } } #ifndef NOCURSESUI if(m_viz) { if(rowkey.m_val != NULL) { m_viz->m_last_key.copy(&rowkey); m_viz->m_last_key.m_isvalid = true; m_viz->m_selection_changed = true; } else { m_viz->m_last_key.m_isvalid = false; } m_viz->m_drilled_up = true; } populate_view_sidemenu(field, &m_sidemenu_viewlist); populate_action_sidemenu(); // // If sorting is different from the default one, restore it // if(sinfo->m_prev_sorting_col != m_views.at(m_selected_view)->m_sortingcol) { m_datatable->set_sorting_col(sinfo->m_prev_sorting_col); } m_datatable->set_is_sorting_ascending(is_sorting_ascending); // // If filtering is different from the default one, apply it // if(m_manual_filter != "" && !m_is_filter_sysdig) { m_datatable->set_freetext_filter(m_manual_filter); } clear(); if(m_viz) { m_viz->render(true); } else if(m_spectro) { m_spectro->render(true); } render(); #endif if(rowkey.m_val) { delete[] rowkey.m_val; } return true; } return false; } void sinsp_cursesui::pause() { m_paused = !m_paused; if(m_datatable != NULL) { m_datatable->set_paused(m_paused); } #ifndef NOCURSESUI if(m_spectro == NULL) { render_header(); } #endif } #ifndef NOCURSESUI void sinsp_cursesui::print_progress(double progress) { attrset(m_colors[sinsp_cursesui::PROCESS]); string wstr = "Processing File"; mvprintw(m_screenh / 2, m_screenw / 2 - wstr.size() / 2, wstr.c_str()); // // Using sprintf because to_string doesn't support setting the precision // char numbuf[64]; sprintf(numbuf, "%.2lf", progress); wstr = "Progress: " + string(numbuf); mvprintw(m_screenh / 2 + 1, m_screenw / 2 - wstr.size() / 2, wstr.c_str()); refresh(); } sysdig_table_action sinsp_cursesui::handle_textbox_input(int ch) { bool closing = false; string* str = NULL; bool handled = true; // // Pick the right string based on what we're doing // if(m_output_filtering) { str = &m_manual_filter; } else if(m_output_searching) { str = &m_manual_search_text; } else { if(m_search_caller_interface) { str = m_search_caller_interface->get_last_search_string(); } else { ASSERT(false); } } switch(ch) { case KEY_F(1): m_mainhelp_page = new curses_mainhelp_page(this); return STA_NONE; case KEY_F(2): m_is_filter_sysdig = !m_is_filter_sysdig; *str = ""; m_cursor_pos = 0; render(); return STA_NONE; case KEY_DOWN: case KEY_UP: case KEY_PPAGE: case KEY_NPAGE: if(m_spy_box != NULL) { m_spy_box->handle_input(ch); } else { if(m_viz) { m_viz->handle_input(ch); } else if(m_spectro) { ASSERT(false); } } return STA_NONE; case 27: // ESC *str = ""; if(m_spy_box != NULL) { m_spy_box->scroll_to(m_search_start_x, m_search_start_y); m_spy_box->up(); } // FALL THROUGH case '\n': case '\r': case KEY_ENTER: case 6: // CTRL+F case KEY_F(4): closing = true; curs_set(0); if(m_is_filter_sysdig && !m_output_searching) { if(*str != "") { sinsp_filter_compiler compiler(m_inspector, *str); sinsp_filter* f; try { f = compiler.compile(); } catch(sinsp_exception e) { // // Backup the cursor position // int cx, cy; getyx(stdscr, cy, cx); // // Print the error string // string wstr = "Invalid sysdig filter"; attrset(m_colors[sinsp_cursesui::FAILED_SEARCH]); mvprintw(m_screenh / 2, m_screenw / 2 - wstr.size() / 2, wstr.c_str()); // // Restore the cursor // attrset(m_colors[PANEL_HIGHLIGHT_FOCUS]); move(cy, cx); curs_set(1); closing = false; break; } delete f; } } break; case KEY_BACKSPACE: case 127: if(str->size() > 0) { m_cursor_pos--; move(m_screenh - 1, m_cursor_pos); addch(' '); move(m_screenh - 1, m_cursor_pos); *str = str->substr(0, str->size() - 1); if(str->size() < 2) { if(m_spy_box != NULL) { m_spy_box->scroll_to(m_search_start_x, m_search_start_y); } } break; } else { return STA_NONE; } case KEY_F(3): if(m_search_caller_interface) { if(m_search_caller_interface->on_search_next()) { render(); } else { string wstr = " NOT FOUND "; attrset(m_colors[sinsp_cursesui::FAILED_SEARCH]); mvprintw(m_screenh / 2, m_screenw / 2 - wstr.size() / 2, wstr.c_str()); render(); } } break; default: handled = false; break; } if(ch >= ' ' && ch <= '~') { addch(ch); *str += ch; m_cursor_pos++; } else { if(!handled) { return STA_NONE; } } if(m_output_filtering) { if(!m_is_filter_sysdig) { // // Update the filter in the datatable // m_datatable->set_freetext_filter(*str); // // Refresh the data and the visualization // m_viz->update_data(m_datatable->get_sample(get_time_delta()), true); m_viz->render(true); } } else if(m_output_searching) { sinsp_table_field* skey = m_datatable->search_in_sample(*str); if(skey != NULL) { int32_t selct = m_datatable->get_row_from_key(skey); m_viz->goto_row(selct); m_search_nomatch = false; } else { m_search_nomatch = true; m_viz->render(true); } } else { if(m_search_caller_interface) { if(m_search_caller_interface->on_search_key_pressed(*str)) { render(); } else { string wstr = " NOT FOUND "; attrset(m_colors[sinsp_cursesui::FAILED_SEARCH]); mvprintw(m_screenh / 2, m_screenw / 2 - wstr.size() / 2, wstr.c_str()); render(); } render(); } else { ASSERT(false); } } if(closing) { sysdig_table_action res = STA_NONE; if(m_is_filter_sysdig && !m_output_searching) { res = STA_SWITCH_VIEW; } m_search_nomatch = false; m_output_filtering = false; m_output_searching = false; m_search_caller_interface = NULL; render(); if(res != STA_NONE) { return res; } } return STA_NONE; } sysdig_table_action sinsp_cursesui::handle_input(int ch) { // // Avoid parsing keys during file load // if((!m_inspector->is_live()) && !is_eof() && (m_spectro != NULL && !m_spectro->m_scroll_paused)) { if(ch != KEY_BACKSPACE && ch != 127 && ch != 'q' && ch != KEY_F(10)) { return STA_NONE; } } if(m_mainhelp_page != NULL) { sysdig_table_action actn = m_mainhelp_page->handle_input(ch); if(actn == STA_DESTROY_CHILD) { delete m_mainhelp_page; m_mainhelp_page = NULL; if(m_spy_box) { m_spy_box->render(); } if(m_viz != NULL) { m_viz->render(true); } else if(m_spectro) { switch_view(false); } if(m_viewinfo_page) { m_viewinfo_page->render(); } render(); return STA_NONE; } else if(actn != STA_PARENT_HANDLE) { return actn; } } if(m_view_sidemenu != NULL) { ASSERT(m_action_sidemenu == NULL); sysdig_table_action ta = m_view_sidemenu->handle_input(ch); if(ta == STA_SWITCH_VIEW) { if(m_viewinfo_page) { delete m_viewinfo_page; m_viewinfo_page = NULL; } return ta; } else if(ta != STA_PARENT_HANDLE) { return STA_NONE; } } else { if(m_action_sidemenu != NULL) { sysdig_table_action ta = m_action_sidemenu->handle_input(ch); if(ta == STA_SWITCH_VIEW) { sinsp_view_info* vinfo = get_selected_view(); g_logger.format("running action %d %s", m_selected_action_sidemenu_entry, vinfo->m_name.c_str()); if(vinfo->m_actions.size() != 0) { ASSERT(m_selected_action_sidemenu_entry < vinfo->m_actions.size()); run_action(&vinfo->m_actions[m_selected_action_sidemenu_entry]); } return ta; } else if(ta == STA_DESTROY_CHILD) { if(m_viz) { m_viz->set_x_start(0); delete m_action_sidemenu; m_action_sidemenu = NULL; m_viz->set_x_start(0); m_viz->recreate_win(m_screenh - 3); m_viz->render(true); m_viz->render(true); } else if(m_spectro) { delete m_action_sidemenu; m_action_sidemenu = NULL; m_spectro->recreate_win(m_screenh - 3); m_spectro->render(true); m_spectro->render(true); } render(); } else if(ta != STA_PARENT_HANDLE) { return STA_NONE; } } if(m_view_sort_sidemenu != NULL) { sysdig_table_action ta = m_view_sort_sidemenu->handle_input(ch); if(ta == STA_SWITCH_VIEW || ta == STA_DESTROY_CHILD) { if(ta == STA_SWITCH_VIEW) { ASSERT(m_selected_view_sort_sidemenu_entry < get_selected_view()->m_columns.size()); m_datatable->set_sorting_col(m_selected_view_sort_sidemenu_entry+1); m_datatable->sort_sample(); m_viz->update_data(m_viz->m_data); } delete m_view_sort_sidemenu; m_view_sort_sidemenu = NULL; m_viz->set_x_start(0); m_viz->recreate_win(m_screenh - 3); m_viz->render(true); render(); if(ta == STA_SWITCH_VIEW) { return STA_NONE; } } else if(ta != STA_PARENT_HANDLE) { return STA_NONE; } } } if(m_output_filtering || m_output_searching || m_search_caller_interface != NULL) { ASSERT(m_view_sidemenu == NULL); ASSERT(m_action_sidemenu == NULL); return handle_textbox_input(ch); } if(m_spy_box != NULL) { ASSERT(m_view_sidemenu == NULL); ASSERT(m_action_sidemenu == NULL); ASSERT(m_output_filtering == false); ASSERT(m_output_searching == false); sysdig_table_action actn = m_spy_box->handle_input(ch); if(actn != STA_PARENT_HANDLE) { return actn; } } // // Note: the info page doesn't handle input when the sidemenu is on, because in that // case it's just going to passively show the info for the selected view // if(m_viewinfo_page && m_view_sidemenu == NULL) { ASSERT(m_view_sidemenu == NULL); sysdig_table_action actn = m_viewinfo_page->handle_input(ch); if(actn == STA_DESTROY_CHILD) { delete m_viewinfo_page; m_viewinfo_page = NULL; if(m_viz != NULL) { m_viz->render(true); } render(); return STA_NONE; } return actn; } // // Pass the event to the table viz // if(m_viz) { sysdig_table_action actn = m_viz->handle_input(ch); if(actn != STA_PARENT_HANDLE) { return actn; } } else if(m_spectro) { sysdig_table_action actn = m_spectro->handle_input(ch); if(actn != STA_PARENT_HANDLE) { return actn; } } switch(ch) { case '?': case 'h': case KEY_F(1): m_mainhelp_page = new curses_mainhelp_page(this); break; case KEY_F(10): case 'q': return STA_QUIT; case 'p': pause(); break; case KEY_F(2): if(m_action_sidemenu != NULL) { break; } if(m_view_sidemenu == NULL) { if(m_viz) { m_viz->set_x_start(VIEW_SIDEMENU_WIDTH); } else if(m_spectro) { m_spectro->set_x_start(VIEW_SIDEMENU_WIDTH); } m_view_sidemenu = new curses_table_sidemenu(curses_table_sidemenu::ST_VIEWS, this, m_selected_view_sidemenu_entry, VIEW_SIDEMENU_WIDTH); m_view_sidemenu->set_entries(&m_sidemenu_viewlist); m_view_sidemenu->set_title("Select View"); render(); m_viewinfo_page = new curses_viewinfo_page(this, m_selected_view, TABLE_Y_START, VIEW_SIDEMENU_WIDTH, m_screenh - TABLE_Y_START - 1, m_screenw - VIEW_SIDEMENU_WIDTH); if(m_spectro) { render(); } } else { if(m_viewinfo_page) { delete m_viewinfo_page; m_viewinfo_page = NULL; } delete m_view_sidemenu; m_view_sidemenu = NULL; if(m_viz) { m_viz->set_x_start(0); m_viz->recreate_win(m_screenh - 3); } else if(m_spectro) { switch_view(false); } render(); } break; case '/': case 6: // CTRL+F m_search_caller_interface = NULL; m_output_searching = true; //m_manual_search_text = ""; m_cursor_pos = 0; curs_set(1); render(); break; case KEY_F(9): case '>': // sort columns if(m_view_sidemenu != NULL) { break; } if(m_view_sort_sidemenu == NULL) { m_viz->set_x_start(VIEW_SIDEMENU_WIDTH); m_sidemenu_sorting_col = m_datatable->get_sorting_col() -1; m_view_sort_sidemenu = new curses_table_sidemenu(curses_table_sidemenu::ST_COLUMNS, this, m_sidemenu_sorting_col, VIEW_SIDEMENU_WIDTH); populate_view_cols_sidemenu(); m_view_sort_sidemenu->set_title("Select sort column"); m_viz->set_x_start(VIEW_SIDEMENU_WIDTH); m_viz->recreate_win(m_screenh - 3); render(); m_viewinfo_page = NULL; } else { m_viz->set_x_start(0); delete m_view_sort_sidemenu; m_view_sort_sidemenu = NULL; m_viz->set_x_start(0); m_viz->recreate_win(m_screenh - 3); m_viz->render(true); m_viz->render(true); render(); } break; case '\\': case KEY_F(4): m_search_caller_interface = NULL; m_output_filtering = true; m_cursor_pos = 0; curs_set(1); render(); break; case KEY_F(5): case 'e': if(m_datatable == NULL) { // // No F5 for non table displays // return STA_NONE; } else if(m_datatable->m_type == sinsp_table::TT_LIST) { // // No F5 for list tables // return STA_NONE; } if(m_datatable->m_sample_data != NULL && m_datatable->m_sample_data->size() != 0) { m_selected_view_sidemenu_entry = 0; m_selected_action_sidemenu_entry = 0; return STA_SPY; } break; case KEY_F(6): case 'd': if(m_datatable == NULL) { // // No F5 for non table displays // return STA_NONE; } else if(m_datatable->m_type == sinsp_table::TT_LIST) { // // No F5 for list tables // return STA_NONE; } if(m_datatable->m_sample_data != NULL && m_datatable->m_sample_data->size() != 0) { m_selected_view_sidemenu_entry = 0; m_selected_action_sidemenu_entry = 0; return STA_DIG; } break; case KEY_F(7): m_viewinfo_page = new curses_viewinfo_page(this, m_selected_view, 0, 0, m_screenh, m_screenw); break; case KEY_F(8): if(m_view_sidemenu != NULL) { break; } if(!m_viz) { ASSERT(false); break; } if(m_action_sidemenu == NULL) { m_viz->set_x_start(ACTION_SIDEMENU_WIDTH); m_action_sidemenu = new curses_table_sidemenu(curses_table_sidemenu::ST_ACTIONS, this, m_selected_action_sidemenu_entry, ACTION_SIDEMENU_WIDTH); populate_action_sidemenu(); m_action_sidemenu->set_title("Select Action"); m_viz->set_x_start(ACTION_SIDEMENU_WIDTH); m_viz->recreate_win(m_screenh - 3); render(); m_viewinfo_page = NULL; } else { m_viz->set_x_start(0); delete m_action_sidemenu; m_action_sidemenu = NULL; m_viz->set_x_start(0); m_viz->recreate_win(m_screenh - 3); m_viz->render(true); m_viz->render(true); render(); } break; case KEY_RESIZE: getmaxyx(stdscr, m_screenh, m_screenw); render(); if(m_spy_box) { m_spy_box->render(); m_spy_box->render(); } if(m_viz != NULL) { m_viz->recreate_win(m_screenh - 3); m_viz->render(true); m_viz->render(true); } else if(m_spectro) { m_spectro->recreate_win(m_screenh - 3); m_spectro->render(true); m_spectro->render(true); } if(m_viewinfo_page) { m_viewinfo_page->render(); m_viewinfo_page->render(); } render(); break; case KEY_MOUSE: { MEVENT* event = NULL; if(m_view_sidemenu != NULL) { event = &m_view_sidemenu->m_last_mevent; } else if(m_view_sort_sidemenu != NULL) { event = &m_view_sort_sidemenu->m_last_mevent; } else if(m_action_sidemenu != NULL) { event = &m_action_sidemenu->m_last_mevent; } else if(m_spy_box != NULL) { event = &m_spy_box->m_last_mevent; } else if(m_viz != NULL) { event = &m_viz->m_last_mevent; } else if(m_spectro != NULL) { event = &m_spectro->m_last_mevent; } if(event == NULL) { ASSERT(false); break; } if(event->bstate & BUTTON1_CLICKED || event->bstate & BUTTON1_DOUBLE_CLICKED) { if((uint32_t)event->y == m_screenh - 1) { int keyc = m_mouse_to_key_list.get_key_from_coordinates(event->x, event->y); if(keyc != -1) { return handle_input(keyc); } } else if((uint32_t)event->y == 1 && (uint32_t)event->x >= m_filterstring_start_x && (uint32_t)event->x <= m_filterstring_end_x) { m_search_caller_interface = NULL; m_is_filter_sysdig = true; m_output_filtering = true; m_manual_filter = m_complete_filter; m_cursor_pos = 0; curs_set(1); render(); } } } break; default: break; } return STA_NONE; } #endif // NOCURSESUI int32_t sinsp_cursesui::get_viewnum_by_name(string name) { for(uint32_t j = 0; j < m_views.size(); ++j) { if(m_views.at(j)->m_id == name) { return j; } } return -1; } // // Note: // - The return value determines if the application should quit. // - res is set to false in case of error // bool sinsp_cursesui::handle_stdin_input(bool* res) { string input; *res = true; // // Get the user json input // while(true) { std::getline(std::cin, input); if(input != "") { break; } } // // Parse the input // Json::Value root; Json::Reader reader; bool pres = reader.parse(input, root, false); if(!pres) { fprintf(stderr, "unable to parse the json input: %s", reader.getFormattedErrorMessages().c_str()); *res = false; return false; } string astr = root["action"].asString(); Json::Value args = root["args"]; sysdig_table_action ta; uint32_t rownum = 0; if(astr == "apply") { ta = STA_SWITCH_VIEW; string vname = args["view"].asString(); m_selected_view = get_viewnum_by_name(vname); if(m_selected_view == -1) { fprintf(stderr, "unknown view: %s", vname.c_str()); *res = false; return false; } } else if(astr == "drilldown") { ta = STA_DRILLDOWN; rownum = args["rownum"].asInt(); } else if(astr == "drillup") { ta = STA_DRILLUP; } else if(astr == "quit") { return true; } else { fprintf(stderr, "invalid action: %s", astr.c_str()); *res = false; return false; } bool tres; execute_table_action(ta, rownum, &tres); return false; } uint64_t sinsp_cursesui::get_time_delta() { if(m_inspector->is_live()) { return m_refresh_interval_ns; } else { return m_last_evt_ts - m_1st_evt_ts; } } void sinsp_cursesui::run_action(sinsp_view_action_info* action) { string resolved_command; bool replacing = false; string fld_to_replace; #ifndef NOCURSESUI ASSERT(m_viz != NULL); ASSERT(m_spectro == NULL); if(m_viz->get_data_size() == 0) { // // No elements in the table means no selection // return; } #endif // NOCURSESUI // // Scan the command string and replace the field names with the values from the selection // for(uint32_t j = 0; j < action->m_command.size(); j++) { char sc = action->m_command[j]; if(sc == '%') { fld_to_replace = ""; if(replacing) { throw sinsp_exception("the following command has the wrong syntax: " + action->m_command); } replacing = true; } else { if(replacing) { if(sc == ' ' || sc == '\t' || sc == '0') { replacing = false; #ifndef NOCURSESUI string val = m_viz->get_field_val(fld_to_replace); resolved_command += val; #endif // NOCURSESUI resolved_command += sc; } else { fld_to_replace += sc; } } else { resolved_command += sc; } } } if(replacing) { #ifndef NOCURSESUI string val = m_viz->get_field_val(fld_to_replace); resolved_command += val; #endif // NOCURSESUI } g_logger.format("original command: %s", action->m_command.c_str()); g_logger.format("running command: %s", resolved_command.c_str()); #ifndef NOCURSESUI // // Exit curses mode // endwin(); #endif // NOCURSESUI // // If needed, ask for confirmation // if(action->m_ask_confirmation) { printf("Confirm command '%s'? [y/N] ", resolved_command.c_str()); fflush(stdout); // // Wait for the enter key // while(int c = getch()) { if(c == -1) { do_sleep(10000); continue; } else if(c == 'y' || c == 'Y') { break; } else { goto action_end; } } } // // Run the command // { int sret = system(resolved_command.c_str()); if(sret == -1) { g_logger.format("command failed"); } } // // If needed, wait for the command to complete // if(action->m_waitfinish) { printf("Command finished. Press ENTER to return to csysdig."); fflush(stdout); // // Wait for the enter key // while(getch() == -1) { do_sleep(10000); } } action_end: // // Empty the keyboard buffer // while(getch() != -1); #ifndef NOCURSESUI // // Reenter curses mode // reset_prog_mode(); // // Refresh the screen // render(); #endif // NOCURSESUI } #ifndef NOCURSESUI bool sinsp_cursesui::is_spectro_paused(int input) { if(m_spectro == NULL) { return false; } if(input == ' ') { m_spectro->m_scroll_paused = false; } return m_spectro->m_scroll_paused; } #endif // NOCURSESUI // // Returns true if the caller should return immediatly after calling us. // In that case, res is filled with the result. // bool sinsp_cursesui::execute_table_action(sysdig_table_action ta, uint32_t rownumber, bool* res) { // // Some events require that we perform additional actions // switch(ta) { case STA_QUIT: *res = true; return true; case STA_SWITCH_VIEW: switch_view(false); *res = false; return true; case STA_SWITCH_SPY: switch_view(true); *res = false; return true; case STA_DRILLDOWN: { #ifndef NOCURSESUI if(m_viz != NULL) { sinsp_view_column_info* kinfo = get_selected_view()->get_key(); // // Note: kinfo is null for list views, which currently don't support // drill down // if(kinfo != NULL) { auto res = m_datatable->get_row_key_name_and_val(m_viz->m_selct, false); if(res.first != NULL) { drilldown(kinfo->get_filter_field(m_view_depth), res.second.c_str(), kinfo, res.first, false); } } } else #endif { if(m_output_type == sinsp_table::OT_CURSES) { drilldown("", "", NULL, NULL, false); } else { sinsp_view_column_info* kinfo = get_selected_view()->get_key(); auto res = m_datatable->get_row_key_name_and_val(rownumber, false); if(res.first != NULL) { drilldown(kinfo->get_filter_field(m_view_depth), res.second.c_str(), kinfo, res.first, false); } } } } *res = false; return true; case STA_DRILLDOWN_TEMPLATE: { sinsp_view_column_info* kinfo = get_selected_view()->get_key(); auto res = m_datatable->get_row_key_name_and_val(0, true); if(res.first != NULL) { drilldown(kinfo->get_filter_field(m_view_depth), res.second.c_str(), kinfo, res.first, true); } } *res = false; return true; case STA_DRILLUP: drillup(); *res = false; return true; #ifndef NOCURSESUI case STA_SPECTRO: case STA_SPECTRO_FILE: { sinsp_view_column_info* kinfo = get_selected_view()->get_key(); // // Note: kinfo is null for list views, that currently don't support // drill down // if(kinfo != NULL) { auto res = m_datatable->get_row_key_name_and_val(m_viz->m_selct, false); if(res.first != NULL) { spectro_selection(get_selected_view()->get_key()->get_filter_field(m_view_depth), res.second.c_str(), get_selected_view()->get_key(), res.first, ta); } } } *res = false; return true; #endif case STA_SPY: { pair res; #ifndef NOCURSESUI if(m_output_type == sinsp_table::OT_CURSES) { res = m_datatable->get_row_key_name_and_val(m_viz->m_selct, false); } else #endif { res = m_datatable->get_row_key_name_and_val(rownumber, false); } if(res.first != NULL) { spy_selection(get_selected_view()->get_key()->get_filter_field(m_view_depth), res.second.c_str(), get_selected_view()->get_key(), false); } } *res = false; return true; case STA_DIG: { #ifndef NOCURSESUI if(m_viz) { auto res = m_datatable->get_row_key_name_and_val(m_viz->m_selct, false); if(res.first != NULL) { spy_selection(get_selected_view()->get_key()->get_filter_field(m_view_depth), res.second.c_str(), get_selected_view()->get_key(), true); } } else #endif { if(m_output_type == sinsp_table::OT_CURSES) { spy_selection("", "", NULL, true); } else { auto res = m_datatable->get_row_key_name_and_val(rownumber, false); if(res.first != NULL) { spy_selection(get_selected_view()->get_key()->get_filter_field(m_view_depth), res.second.c_str(), get_selected_view()->get_key(), true); } } } } *res = false; return true; case STA_NONE: break; default: ASSERT(false); break; } return false; } #endif // CSYSDIG sysdig-0.26.4/userspace/libsinsp/cursesui.h000066400000000000000000000417451352731327100207540ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #ifdef CSYSDIG #ifndef _WIN32 #include #endif #define UI_USER_INPUT_CHECK_PERIOD_NS 10000000 #define VIEW_SIDEMENU_WIDTH 20 #define ACTION_SIDEMENU_WIDTH 30 #define FILTER_TEMPLATE_MAGIC "@#$f1CA^&;" string combine_filters(string flt1, string flt2); class ctext; class sinsp_chart; class curses_spectro; extern sinsp_logger g_logger; class sinsp_menuitem_info { public: enum type { TABLE = 1, LIST = 2, ALL = TABLE | LIST, }; sinsp_menuitem_info(string key, string desc, sinsp_menuitem_info::type type, int keyboard_equivalent) { m_key = key; m_desc = desc; m_type = type; m_keyboard_equivalent = keyboard_equivalent; } string m_key; string m_desc; sinsp_menuitem_info::type m_type; int m_keyboard_equivalent; }; class sinsp_ui_selection_info { public: sinsp_ui_selection_info(string field, string val, sinsp_view_column_info* column_info, string view_filter, uint32_t prev_selected_view, uint32_t prev_selected_sidemenu_entry, sinsp_table_field* rowkey, uint32_t prev_sorting_col, string prev_manual_filter, bool prev_is_filter_sysdig, bool prev_is_sorting_ascending, bool is_drilldown) { m_field = field; m_column_info = column_info; m_val = val; m_view_filter = view_filter; m_prev_selected_view = prev_selected_view; m_prev_selected_sidemenu_entry = prev_selected_sidemenu_entry; m_prev_sorting_col = prev_sorting_col; m_prev_manual_filter = prev_manual_filter; m_prev_is_filter_sysdig = prev_is_filter_sysdig; m_prev_is_sorting_ascending = prev_is_sorting_ascending; m_is_drilldown = is_drilldown; if(rowkey != NULL) { m_rowkey = *rowkey; } else { m_rowkey.m_len = 0; m_rowkey.m_val = NULL; } } string m_field; string m_val; sinsp_view_column_info* m_column_info; string m_view_filter; uint32_t m_prev_selected_view; uint32_t m_prev_selected_sidemenu_entry; uint32_t m_prev_sorting_col; string m_prev_manual_filter; bool m_prev_is_filter_sysdig; sinsp_table_field m_rowkey; bool m_prev_is_sorting_ascending; bool m_is_drilldown; }; class sinsp_ui_selection_hierarchy { public: void push_back(string field, string val, sinsp_view_column_info* column_info, string view_filter, uint32_t prev_selected_view, uint32_t prev_selected_sidemenu_entry, sinsp_table_field* rowkey, uint32_t prev_sorting_col, string prev_manual_filter, bool prev_is_filter_sysdig, bool prev_is_sorting_ascending, bool is_drilldown) { m_hierarchy.push_back(sinsp_ui_selection_info(field, val, column_info, view_filter, prev_selected_view, prev_selected_sidemenu_entry, rowkey, prev_sorting_col, prev_manual_filter, prev_is_filter_sysdig, prev_is_sorting_ascending, is_drilldown)); } ~sinsp_ui_selection_hierarchy() { for(auto e : m_hierarchy) { if(e.m_rowkey.m_val != NULL) { delete [] e.m_rowkey.m_val; } } } string tofilter(bool templated) { string res; uint32_t j; uint32_t hs = (uint32_t)m_hierarchy.size(); for(j = 0; j < hs; j++) { bool has_filter = false; uint32_t lastsize = res.size(); if(m_hierarchy[j].m_view_filter != "") { has_filter = true; } if(hs > 1) { res += "("; } if(has_filter) { res += "("; res += m_hierarchy[j].m_view_filter; res += ")"; } if(m_hierarchy[j].m_field != "") { bool skip = false; if(m_hierarchy[j].m_column_info != NULL && (m_hierarchy[j].m_column_info->m_flags & TEF_FILTER_IN_CHILD_ONLY)) { if(j < hs - 1) { skip = true; } } if(!skip) { if(has_filter) { res += " and "; } res += m_hierarchy[j].m_field; res += "="; if(templated && (j == hs - 1)) { res += string("\"") + FILTER_TEMPLATE_MAGIC + "\""; } else { res += m_hierarchy[j].m_val; } } } if(res.size() != lastsize) { if(hs > 1) { res += ")"; } res += " and "; if(res.size() >= 7 && res.substr(res.size() - 7) == "() and ") { res = res.substr(0, res.size() - 7); } } } if(res.size() >= 5) { string trailer = res.substr(res.size() - 5).c_str(); if(trailer == " and ") { res = res.substr(0, res.size() - 5); } } return res; } uint32_t size() { return (uint32_t)m_hierarchy.size(); } sinsp_ui_selection_info* at(uint32_t j) { return &m_hierarchy[j]; } bool pop_back() { if(m_hierarchy.size() == 0) { return false; } else { m_hierarchy.pop_back(); return true; } } private: vector m_hierarchy; }; class sinsp_mouse_to_key_list_entry { public: sinsp_mouse_to_key_list_entry(uint32_t startx, uint32_t starty, uint32_t endx, uint32_t endy, int keyboard_equivalent) { m_startx = startx; m_endx = endx; m_starty = starty; m_endy = endy; m_keyboard_equivalent = keyboard_equivalent; } uint32_t m_startx; uint32_t m_endx; uint32_t m_starty; uint32_t m_endy; int m_keyboard_equivalent; }; class sinsp_mouse_to_key_list { public: void add(sinsp_mouse_to_key_list_entry entry) { m_list.push_back(entry); } int get_key_from_coordinates(uint32_t x, uint32_t y) { for(auto e : m_list) { if(x >= e.m_startx && x <= e.m_endx && y >= e.m_starty && y <= e.m_endy) { return e.m_keyboard_equivalent; } } return -1; } void clear() { m_list.clear(); } vector m_list; }; class json_spy_renderer { public: json_spy_renderer(sinsp* inspector, sinsp_cursesui* parent, int32_t viz_type, spy_text_renderer::sysdig_output_type sotype, bool print_containers, sinsp_evt::param_fmt text_fmt); ~json_spy_renderer(); void set_filter(string filter); void process_event(sinsp_evt* evt, int32_t next_res); string get_data(); uint64_t get_count(); private: void process_event_spy(sinsp_evt* evt, int32_t next_res); void process_event_dig(sinsp_evt* evt, int32_t next_res); spy_text_renderer* m_json_spy_renderer; sinsp* m_inspector; Json::Value m_root; sinsp_filter* m_filter; uint64_t m_linecnt; }; class sinsp_cursesui { public: enum ColorElements_ { RESET_COLOR, DEFAULT_COLOR, FUNCTION_BAR, FUNCTION_KEY, FAILED_SEARCH, PANEL_HEADER_FOCUS, PANEL_HEADER_UNFOCUS, PANEL_HIGHLIGHT_FOCUS, PANEL_HIGHLIGHT_UNFOCUS, PANEL_HEADER_LIST_FOCUS, PANEL_HEADER_LIST_HIGHLIGHT, LARGE_NUMBER, METER_TEXT, METER_VALUE, LED_COLOR, UPTIME, BATTERY, TASKS_RUNNING, SWAP, PROCESS, PROCESS_SHADOW, PROCESS_TAG, PROCESS_MEGABYTES, PROCESS_TREE, PROCESS_R_STATE, PROCESS_D_STATE, PROCESS_BASENAME, PROCESS_HIGH_PRIORITY, PROCESS_LOW_PRIORITY, PROCESS_THREAD, PROCESS_THREAD_BASENAME, BAR_BORDER, BAR_SHADOW, GRAPH_BLACK, GRAPH_WHITE, GRAPH_WHITE_D, GRAPH_GREEN_L, GRAPH_GREEN, GRAPH_GREEN_D, GRAPH_YELLOW_L, GRAPH_YELLOW, GRAPH_YELLOW_D, GRAPH_RED_L, GRAPH_RED, GRAPH_RED_D, GRAPH_MAGENTA_L, GRAPH_MAGENTA, MEMORY_USED, MEMORY_BUFFERS, MEMORY_BUFFERS_TEXT, MEMORY_CACHE, LOAD, LOAD_AVERAGE_FIFTEEN, LOAD_AVERAGE_FIVE, LOAD_AVERAGE_ONE, CHECK_BOX, CHECK_MARK, CHECK_TEXT, CLOCK, HELP_BOLD, HOSTNAME, CPU_NICE, CPU_NICE_TEXT, CPU_NORMAL, CPU_KERNEL, CPU_IOWAIT, CPU_IRQ, CPU_SOFTIRQ, SPY_READ, SPY_WRITE, LAST_COLORELEMENT }; sinsp_cursesui(sinsp* inspector, string event_source_name, string cmdline_capture_filter, uint64_t refresh_interval_ns, bool print_containers, sinsp_table::output_type output_type, bool is_mousedrag_available, int32_t json_first_row, int32_t json_last_row, int32_t sorting_col, sinsp_evt::param_fmt json_spy_text_fmt); ~sinsp_cursesui(); void configure(sinsp_view_manager* views); void start(bool is_drilldown, bool is_spy_switch); sinsp_view_info* get_selected_view(); sinsp_view_info* get_prev_selected_view(); void pause(); bool is_searching() { return m_output_filtering; } bool is_eof() { return m_eof != 0; } void set_truncated_input(bool truncated) { m_truncated_input = truncated; } void set_interactive(bool interactive) { m_interactive = interactive; } #ifndef NOCURSESUI void render(); #endif void turn_search_on(search_caller_interface* ifc, string header_text); uint64_t get_time_delta(); void run_action(sinsp_view_action_info* action); void spy_selection(string field, string val, sinsp_view_column_info* column_info, bool is_dig); sysdig_table_action handle_input(int ch); bool handle_stdin_input(bool* res); // // Return true if the application is supposed to exit // inline bool process_event(sinsp_evt* evt, int32_t next_res) { uint64_t ts = evt->get_ts(); if(!m_inspector->is_live()) { if(m_1st_evt_ts == 0) { m_1st_evt_ts = ts; } m_last_evt_ts = ts; } // // Process the user input // if(m_output_type != sinsp_table::OT_JSON) { #ifndef NOCURSESUI if((ts - m_last_input_check_ts > m_input_check_period_ns) || m_eof) { uint32_t ninputs = 0; uint64_t evtnum = evt->get_num(); // // If this is a file, print the progress once in a while // if(!m_inspector->is_live() && !m_offline_replay) { if(evtnum - m_last_progress_evt > 30000) { print_progress(m_inspector->get_read_progress()); m_last_progress_evt = evtnum; } } // // If we have more than one event in the queue, consume all of them // while(true) { int input = getch(); bool sppaused = is_spectro_paused(input); if(input == -1) { // // All events consumed // if(m_spectro) { if(sppaused) { usleep(100000); continue; } else { break; } } else { break; } } else { ninputs++; } // // Handle the event // sysdig_table_action ta = handle_input(input); bool res; if(execute_table_action(ta, 0, &res) == true) { return res; } } if(ninputs == 0) { m_last_input_check_ts = ts; } } #endif } else { // // JSON output. // If this is a file, print the progress once in a while // if(!m_inspector->is_live() && !m_offline_replay) { uint64_t evtnum = evt->get_num(); if(evtnum - m_last_progress_evt > 300000) { printf("{\"progress\": %.2lf},\n", m_inspector->get_read_progress()); fflush(stdout); m_last_progress_evt = evtnum; } } } // // We were reading from a file and we reached its end. // Unless we are in raw mode, we keep looping because we want to handle user events, // but we stop the processing here. We also make sure to sleep a bit to keep the // CPU under control. // if(m_eof > 1) { #ifndef NOCURSESUI usleep(10000); #endif if(m_output_type == sinsp_table::OT_RAW || m_output_type == sinsp_table::OT_JSON) { if(m_interactive) { bool sres; return handle_stdin_input(&sres); } else { return true; } } else { return false; } } // // Perform event processing // #ifndef NOCURSESUI if(m_spy_box) { m_spy_box->process_event(evt, next_res); } else #endif if(m_json_spy_renderer) { m_json_spy_renderer->process_event(evt, next_res); uint64_t evtnum = evt->get_num(); if((evtnum - m_last_progress_evt > 2000) || (next_res == SCAP_EOF)) { string jdata = m_json_spy_renderer->get_data(); double rprogress = m_inspector->get_read_progress(); printf("{\"progress\": %.2lf,", rprogress); if(next_res == SCAP_EOF) { printf(" \"count\": %" PRIu64 ", ", m_json_spy_renderer->get_count()); } printf("\"data\": %s", jdata.c_str()); printf("}"); if(next_res != SCAP_EOF) { printf(","); } fflush(stdout); m_last_progress_evt = evtnum; } // // Check if this the end of the capture file, and if yes take note of that // if(next_res == SCAP_EOF) { ASSERT(!m_inspector->is_live()); m_eof++; return true; } } else { bool end_of_sample; // // Check if it's time to flush // if(m_inspector->is_live() || m_offline_replay) { if(next_res == SCAP_EOF) { end_of_sample = true; } else { end_of_sample = (evt == NULL || ts > m_datatable->m_next_flush_time_ns); } } else { // // For files, we flush only once, at the end of the capture. // if(next_res == SCAP_EOF) { end_of_sample = true; } else { end_of_sample = false; } } if(end_of_sample) { handle_end_of_sample(evt, next_res); // // Check if this the end of the capture file, and if yes take note of that // if(next_res == SCAP_EOF) { ASSERT(!m_inspector->is_live()); m_eof++; return false; } } m_datatable->process_event(evt); } return false; } sinsp_table* m_datatable; int m_colors[LAST_COLORELEMENT]; sinsp_view_manager m_views; int32_t m_selected_view; int32_t m_prev_selected_view; uint32_t m_selected_view_sidemenu_entry; uint32_t m_selected_action_sidemenu_entry; uint32_t m_selected_view_sort_sidemenu_entry; sinsp_ui_selection_hierarchy m_sel_hierarchy; uint32_t m_screenw; uint32_t m_screenh; uint32_t m_eof; uint64_t m_input_check_period_ns; bool m_search_nomatch; bool m_print_containers; int32_t m_sidemenu_sorting_col; #ifndef NOCURSESUI curses_table* m_viz; curses_spectro* m_spectro; curses_table_sidemenu* m_view_sidemenu; curses_table_sidemenu* m_action_sidemenu; curses_viewinfo_page* m_viewinfo_page; curses_table_sidemenu* m_view_sort_sidemenu; curses_mainhelp_page* m_mainhelp_page; curses_textbox* m_spy_box; sinsp_evt::param_fmt m_spybox_text_format; #endif bool m_offline_replay; uint64_t m_refresh_interval_ns; sinsp* m_inspector; uint32_t m_view_depth; bool m_is_mousedrag_available; private: Json::Value generate_json_info_section(); void handle_end_of_sample(sinsp_evt* evt, int32_t next_res); void restart_capture(bool is_spy_switch); void switch_view(bool is_spy_switch); bool spectro_selection(string field, string val, sinsp_view_column_info* column_info, filtercheck_field_info* info, sysdig_table_action ta); bool do_drilldown(string field, string val, sinsp_view_column_info* column_info, uint32_t new_view_num, filtercheck_field_info* info, bool dont_restart); // returns false if there is no suitable drill down view for this field bool drilldown(string field, string val, sinsp_view_column_info* column_info, filtercheck_field_info* info, bool dont_restart); // returns false if we are already at the top of the hierarchy bool drillup(); void create_complete_filter(bool templated); bool execute_table_action(sysdig_table_action ta, uint32_t rownumber, bool* res); int32_t get_viewnum_by_name(string name); #ifndef NOCURSESUI void render_header(); void draw_bottom_menu(vector* items, bool istable); void render_default_main_menu(); void render_filtersearch_main_menu(); void render_spy_main_menu(); void render_position_info(); void render_main_menu(); sysdig_table_action handle_textbox_input(int ch); void populate_view_sidemenu(string field, vector* viewlist); void populate_action_sidemenu(); void populate_view_cols_sidemenu(); void print_progress(double progress); void show_selected_view_info(); bool is_spectro_paused(int input); #endif vector m_menuitems; vector m_menuitems_spybox; string m_event_source_name; string m_cmdline_capture_filter; string m_complete_filter; string m_complete_filter_noview; string m_manual_filter; string m_manual_search_text; bool m_paused; uint64_t m_last_input_check_ts; bool m_output_filtering; bool m_output_searching; uint32_t m_cursor_pos; bool m_is_filter_sysdig; uint64_t m_last_progress_evt; vector m_sidemenu_viewlist; sinsp_chart* m_chart; search_caller_interface* m_search_caller_interface; int32_t m_search_start_x, m_search_start_y; uint64_t m_n_evts_in_file; uint64_t m_1st_evt_ts; uint64_t m_last_evt_ts; uint64_t m_evt_ts_delta; sinsp_filter_check_reference* m_timedelta_formatter; sinsp_mouse_to_key_list m_mouse_to_key_list; uint32_t m_filterstring_start_x; uint32_t m_filterstring_end_x; string m_search_header_text; sinsp_table::output_type m_output_type; bool m_truncated_input; bool m_interactive; int32_t m_json_first_row; int32_t m_json_last_row; int32_t m_sorting_col; json_spy_renderer* m_json_spy_renderer; sinsp_evt::param_fmt m_json_spy_text_fmt; }; #endif // CSYSDIG sysdig-0.26.4/userspace/libsinsp/cyclewriter.cpp000066400000000000000000000132101352731327100217630ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "sinsp.h" #include "sinsp_int.h" #include "cyclewriter.h" cycle_writer::cycle_writer(bool is_live) : m_base_file_name(""), m_rollover_mb(0L), m_duration_seconds(0), m_file_limit(0), m_event_limit(0L), m_last_time(0), m_file_count_total(0), m_file_index(0), m_first_consider(false), m_event_count(0L), m_dumper(NULL), m_past_names(NULL) { // // null terminate the first // character of the limit format // to say that we want things to // be created when we consider() the // next file. // m_limit_format[0] = 0; this->live = is_live; } bool cycle_writer::setup(string base_file_name, int rollover_mb, int duration_seconds, int file_limit, unsigned long event_limit, scap_dumper_t** dumper) { if(m_first_consider) { return false; } m_base_file_name = base_file_name; m_rollover_mb = rollover_mb * 1000000L; m_duration_seconds = duration_seconds; m_file_limit = file_limit; m_event_limit = event_limit; m_dumper = dumper; if(duration_seconds > 0 && file_limit > 0) { m_past_names = new string[file_limit]; for(int32_t j = 0; j < file_limit; j++) { m_past_names[j] = ""; } } // // Seed the filename with an initial // value. // consider(NULL); return true; } // // consider a certain number of bytes given the parameters // passed in through setup. Consider will recommend one // of the following: // // * SAMEFILE - use the same file // * NEWFILE - use a new file (inquiry with get_current_file_name()) // * DOQUIT - end the capture. // cycle_writer::conclusion cycle_writer::consider(sinsp_evt* evt) { if(m_first_consider == false) { m_first_consider = true; } if(evt == NULL) // First run { if(!live && m_duration_seconds > 0 && m_base_file_name.find("%") != string::npos) // Here's the fuckin' bug m_last_file_name = "first_dump.scap"; else { if(live) m_last_time = time(NULL); next_file(); } return NEWFILE; } m_event_count++; if(m_duration_seconds > 0) { // // If this is our first consideration, // we set the timer up. // if(m_last_time == 0) { m_last_time = evt->get_ts() / 1000000000; // 10^(-9) because it's nanoseconds } if((int)difftime(evt->get_ts() / 1000000000, m_last_time) >= m_duration_seconds) { m_last_time = evt->get_ts() / 1000000000; m_last_reason = "Maximum Time Reached"; return next_file(); } } if(m_rollover_mb > 0 && scap_dump_get_offset(*m_dumper) > m_rollover_mb) { m_last_reason = "Maximum File Size Reached"; return next_file(); } // Event limit if(m_event_limit > 0 && m_event_count >= m_event_limit) { m_event_count = 0L; m_last_reason = "Maximum Event Number Reached"; return next_file(); } // // This is for any routine which restricts // execution after an initial consider() // /*if(m_first_consider == false) { m_first_consider = true; // We need to generate an initial file name // but still continue our logic. next_file(); }*/ return SAMEFILE; } string cycle_writer::get_current_file_name() { return m_last_file_name; } // // next_file doesn't return the file pointer // instead it returns advice on whether a new // file should be used or not. // // If it advices a new file, then the new file // name advised can be found in the // get_current_file_name() routine. // cycle_writer::conclusion cycle_writer::next_file() { if (m_file_limit > 0 && m_file_index >= m_file_limit) { m_file_index = 0; } if(m_duration_seconds > 0) { // if the user has specified a format then use it if(m_base_file_name.find("%") != string::npos) { const size_t our_size = 4096; size_t their_size; char file_name[our_size]; const struct tm *our_time = localtime(&m_last_time); their_size = strftime(file_name, our_size, m_base_file_name.c_str(), our_time); if(their_size == 0) {/* TODO: if fail but as string size has been increased to 4096 it's very unlikely we get here */ } if(m_file_limit > 0) { if(m_past_names[m_file_index] != "") { remove(m_past_names[m_file_index].c_str()); } m_past_names[m_file_index] = string(file_name); } m_last_file_name = file_name; } else // if no format is provided, then use a counter { m_last_file_name = m_base_file_name + to_string(m_file_index); } } else { m_last_file_name = m_base_file_name; } if(m_rollover_mb > 0) { if(m_limit_format[0] == 0) // I have no idea if this part is executed and, if so, if it works correctly { int digit_count = 0; int our_file_limit = m_file_limit; while(our_file_limit > 0) { digit_count++; our_file_limit /= 10; } snprintf( // The format we are trying to derive m_limit_format, sizeof(m_limit_format), // // Read the string below like this: // // %05d // // Which is what we want. // "%%0%dd", digit_count ); } char index[22]; snprintf(index, sizeof(index), m_limit_format, m_file_index); m_last_file_name += index; } if(m_event_limit > 0) { m_last_file_name = m_base_file_name + to_string(m_file_index); } m_file_count_total++; m_file_index++; return NEWFILE; } sysdig-0.26.4/userspace/libsinsp/cyclewriter.h000066400000000000000000000077441352731327100214470ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include using namespace std; class cycle_writer { public: // // The conclusion is what consider() tells you to do // after you tell it how many more bytes to consider. // // We don't deal directly with file pointers here to // keep the concerns separated. This engine only advises // a course of action. // enum conclusion { // Continue to use the same file SAMEFILE, // Close the current file handle // and use the one in get_current_file_name() NEWFILE, // It's the end of the capture, // close the file handle and exit. // (actually never used, but kept for future // implementations and backwards compatibility) DOQUIT }; cycle_writer(bool is_live); ~cycle_writer() { if(m_past_names != NULL) { delete[] m_past_names; } }; // // Setup sets all the parameters of the // engine in one go so you don't miss anything. // // Also, if the engine has already started // (via a call to consider()), then this will // be locked down and return false. // bool setup(string base_file_name, int rollover_mb, int duration_seconds, int file_limit, unsigned long event_limit, scap_dumper_t** dumper); // // Consider file size at the current time // and tell us whether // // * a new file should be written, // * we should use the same file, or // * we should quit. // // If we should use a new file, or should quit, // then m_last_reason will tell us why consider // thought so, and in the case of a new file, // get_current_file_name() will tell us the new // capture file name to use. // cycle_writer::conclusion consider(sinsp_evt* evt); // // The yields the current file name // based on the input parameters and // what has been past into consider. // string get_current_file_name(); // Last reason for a new file string m_last_reason; private: // // This will yield a new file if // needed or conclude that we need // to exit. // cycle_writer::conclusion next_file(); // // These are the variables that are set // to specify how the engine will work. // // Use the setup() function to set them up. // // values <= 0 mean don't use the feature. // // The base file name to write to string m_base_file_name; // = "" // Number of bytes before rolling over int64_t m_rollover_mb; // = 0 // Time in seconds between captures int m_duration_seconds; // = 0 // Total number of allowed captures int m_file_limit; // = 0 // Event division: every how many event // should I split the file? unsigned long m_event_limit; // = 0L private: // Last time of a capture time_t m_last_time; // Total number of files written int m_file_count_total; // Current index int m_file_index; // // This is the 0-left padded format // for creating file names. Since // we don't know what's what at first // we just leave this hanging. // char m_limit_format[6]; // The last file name that // was created (mostly for debugging) string m_last_file_name; // // This is toggled to true the // first time consider is run ... // it will lock the setup() from // being further run // bool m_first_consider; // = false // number of events unsigned long m_event_count; // = 0L scap_dumper_t** m_dumper; bool live; // Used when ciclewriting on time with specified // name format for deleting "out-of-range" files string *m_past_names; }; sysdig-0.26.4/userspace/libsinsp/dns_manager.cpp000066400000000000000000000115641352731327100217170ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "dns_manager.h" void sinsp_dns_resolver::refresh(uint64_t erase_timeout, uint64_t base_refresh_timeout, uint64_t max_refresh_timeout, std::future f_exit) { #if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT) sinsp_dns_manager &manager = sinsp_dns_manager::get(); while(true) { if(!manager.m_cache.empty()) { std::list to_delete; uint64_t ts = sinsp_utils::get_current_time_ns(); for(auto &it: manager.m_cache) { const std::string &name = it.first; sinsp_dns_manager::dns_info &info = it.second; if((ts > info.m_last_used_ts) && (ts - info.m_last_used_ts) > erase_timeout) { // remove the entry if it's hasn't been used for a whole hour to_delete.push_back(name); } else if(ts > (info.m_last_resolve_ts + info.m_timeout)) { sinsp_dns_manager::dns_info refreshed_info = manager.resolve(name, ts); refreshed_info.m_timeout = base_refresh_timeout; refreshed_info.m_last_resolve_ts = info.m_last_resolve_ts = ts; // dns_info::operator!= will check if some // v4 or v6 addresses are changed from the // last resolution if(refreshed_info != info) { info = refreshed_info; } else if(info.m_timeout < max_refresh_timeout) { // double the timeout until 320 secs info.m_timeout <<= 1; } } } if(!to_delete.empty()) { manager.m_erase_mutex.lock(); for(const auto &name : to_delete) { manager.m_cache.unsafe_erase(name); } manager.m_erase_mutex.unlock(); } } if(f_exit.wait_for(std::chrono::nanoseconds(base_refresh_timeout)) == std::future_status::ready) { break; } } #endif } #if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT) inline sinsp_dns_manager::dns_info sinsp_dns_manager::resolve(const std::string &name, uint64_t ts) { dns_info dinfo; struct addrinfo hints, *result, *rp; memset(&hints, 0, sizeof(struct addrinfo)); // Allow IPv4 or IPv6, all socket types, all protocols hints.ai_family = AF_UNSPEC; int s = getaddrinfo(name.c_str(), NULL, &hints, &result); if (!s && result) { for (rp = result; rp != NULL; rp = rp->ai_next) { if(rp->ai_family == AF_INET) { dinfo.m_v4_addrs.insert(((struct sockaddr_in*)rp->ai_addr)->sin_addr.s_addr); } else // AF_INET6 { ipv6addr v6; memcpy(v6.m_b, ((struct sockaddr_in6*)rp->ai_addr)->sin6_addr.s6_addr, sizeof(ipv6addr)); dinfo.m_v6_addrs.insert(v6); } } freeaddrinfo(result); } return dinfo; } #endif bool sinsp_dns_manager::match(const char *name, int af, void *addr, uint64_t ts) { #if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT) if(!m_resolver) { m_resolver = new thread(sinsp_dns_resolver::refresh, m_erase_timeout, m_base_refresh_timeout, m_max_refresh_timeout, m_exit_signal.get_future()); } string sname = string(name); m_erase_mutex.lock(); if(m_cache.find(sname) == m_cache.end()) { dns_info dinfo = resolve(sname, ts); dinfo.m_timeout = m_base_refresh_timeout; dinfo.m_last_resolve_ts = ts; m_cache[sname] = dinfo; } m_cache[sname].m_last_used_ts = ts; dns_info &dinfo = m_cache[sname]; m_erase_mutex.unlock(); if(af == AF_INET6) { ipv6addr v6; memcpy(v6.m_b, addr, sizeof(ipv6addr)); return dinfo.m_v6_addrs.find(v6) != dinfo.m_v6_addrs.end(); } else if(af == AF_INET) { return dinfo.m_v4_addrs.find(*(uint32_t *)addr) != dinfo.m_v4_addrs.end(); } #endif return false; } string sinsp_dns_manager::name_of(int af, void *addr, uint64_t ts) { string ret; #if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT) if(!m_cache.empty()) { m_erase_mutex.lock(); for(auto &it: m_cache) { const std::string &name = it.first; sinsp_dns_manager::dns_info &info = it.second; if(af == AF_INET6) { ipv6addr v6; memcpy(v6.m_b, addr, sizeof(ipv6addr)); if (info.m_v6_addrs.find(v6) != info.m_v6_addrs.end()) { info.m_last_used_ts = ts; ret = name; break; } } else if(af == AF_INET && info.m_v4_addrs.find(*(uint32_t *)addr) != info.m_v4_addrs.end()) { info.m_last_used_ts = ts; ret = name; break; } } m_erase_mutex.unlock(); } #endif return ret; } void sinsp_dns_manager::cleanup() { if(m_resolver) { m_exit_signal.set_value(); m_resolver->join(); m_resolver = NULL; m_exit_signal = std::promise(); } } sysdig-0.26.4/userspace/libsinsp/dns_manager.h000066400000000000000000000061721352731327100213630ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #if defined(_WIN64) || defined(WIN64) || defined(_WIN32) || defined(WIN32) #include #else #include #include #endif #include #include #include #include #include #if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT) #include "tbb/concurrent_unordered_map.h" #endif #include "sinsp.h" struct sinsp_dns_resolver { static void refresh(uint64_t erase_timeout, uint64_t base_refresh_timeout, uint64_t max_refresh_timeout, std::future f_exit); }; class sinsp_dns_manager { public: bool match(const char *name, int af, void *addr, uint64_t ts); string name_of(int af, void *addr, uint64_t ts); void cleanup(); static sinsp_dns_manager& get() { static sinsp_dns_manager instance; return instance; }; void set_erase_timeout(uint64_t ns) { m_erase_timeout = ns; }; void set_base_refresh_timeout(uint64_t ns) { m_base_refresh_timeout = ns; }; void set_max_refresh_timeout(uint64_t ns) { m_max_refresh_timeout = ns; }; size_t size() { #if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT) return m_cache.size(); #else return 0; #endif }; private: sinsp_dns_manager() : m_erase_timeout(3600 * ONE_SECOND_IN_NS), m_base_refresh_timeout(10 * ONE_SECOND_IN_NS), m_max_refresh_timeout(320 * ONE_SECOND_IN_NS) {}; sinsp_dns_manager(sinsp_dns_manager const&) = delete; void operator=(sinsp_dns_manager const&) = delete; #if defined(HAS_CAPTURE) && !defined(CYGWING_AGENT) struct dns_info { bool operator==(const dns_info &other) const { return m_v4_addrs == other.m_v4_addrs && m_v6_addrs == other.m_v6_addrs; }; bool operator!=(const dns_info &other) const { return !operator==(other); }; uint64_t m_timeout; uint64_t m_last_resolve_ts; uint64_t m_last_used_ts; std::set m_v4_addrs; std::set m_v6_addrs; }; static inline dns_info resolve(const std::string &name, uint64_t ts); typedef tbb::concurrent_unordered_map c_dns_table; c_dns_table m_cache; #endif // tbb concurrent unordered map is not thread-safe for deletions, // so we still need a mutex, but the chances of waiting are really // low, since we will almost never do an erase. std::mutex m_erase_mutex; // used to let m_resolver know when to terminate std::promise m_exit_signal; std::thread *m_resolver; uint64_t m_erase_timeout; uint64_t m_base_refresh_timeout; uint64_t m_max_refresh_timeout; friend sinsp_dns_resolver; }; sysdig-0.26.4/userspace/libsinsp/doxygen/000077500000000000000000000000001352731327100204035ustar00rootroot00000000000000sysdig-0.26.4/userspace/libsinsp/doxygen/conf.dox000066400000000000000000000264561352731327100220610ustar00rootroot00000000000000# Doxyfile 1.8.6 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = libsinsp PROJECT_NUMBER = 0.1 PROJECT_BRIEF = "A C++ library for system inspection" PROJECT_LOGO = OUTPUT_DIRECTORY = CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 ALIASES = TCL_SUBST = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES AUTOLINK_SUPPORT = YES BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES INLINE_GROUPED_CLASSES = NO INLINE_SIMPLE_STRUCTS = NO TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_PACKAGE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = NO HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES SHOW_GROUPED_MEMB_INC = NO FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = LAYOUT_FILE = CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- INPUT = ../sinsp.h ../event.h ../eventformatter.h ../dumper.h ../filter.h ../threadinfo.h ../fdinfo.h ../tuples.h ../../../driver/ppm_events_public.h INPUT_ENCODING = UTF-8 FILE_PATTERNS = RECURSIVE = NO EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = erase_fd_params sinsp_fdtable sinsp_thread_manager sinsp_thread_privatestate_manager ppm_evt_hdr EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES USE_HTAGS = NO VERBATIM_HEADERS = YES CLANG_ASSISTED_PARSING = NO CLANG_OPTIONS = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = header.html HTML_FOOTER = footer.html HTML_STYLESHEET = HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = YES HTML_DYNAMIC_SECTIONS = NO HTML_INDEX_NUM_ENTRIES = 100 GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO GENERATE_QHP = NO QCH_FILE = QHP_NAMESPACE = org.doxygen.Project QHP_VIRTUAL_FOLDER = doc QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = QHG_LOCATION = GENERATE_ECLIPSEHELP = NO ECLIPSE_DOC_ID = org.doxygen.Project DISABLE_INDEX = NO GENERATE_TREEVIEW = NO ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES USE_MATHJAX = NO MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest MATHJAX_EXTENSIONS = MATHJAX_CODEFILE = SEARCHENGINE = NO SERVER_BASED_SEARCH = NO EXTERNAL_SEARCH = NO SEARCHENGINE_URL = SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4 EXTRA_PACKAGES = LATEX_HEADER = LATEX_FOOTER = LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # Configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = HAS_FILTERING _DOXYGEN EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration options related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = NO MSCGEN_PATH = DIA_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO DOT_NUM_THREADS = 0 DOT_FONTNAME = Helvetica DOT_FONTSIZE = 10 DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO UML_LIMIT_NUM_FIELDS = 10 TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png INTERACTIVE_SVG = NO DOT_PATH = DOTFILE_DIRS = MSCFILE_DIRS = DIAFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES sysdig-0.26.4/userspace/libsinsp/doxygen/footer.html000066400000000000000000000000301352731327100225600ustar00rootroot00000000000000
sysdig-0.26.4/userspace/libsinsp/doxygen/header.html000066400000000000000000000024221352731327100225210ustar00rootroot00000000000000--- layout: default title: sysdig | libsinsp ---

$projectname $projectnumber

(Generated by Doxygen)

$projectbrief

$projectbrief
$searchbox
sysdig-0.26.4/userspace/libsinsp/dumper.cpp000066400000000000000000000076731352731327100207430ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "sinsp.h" #include "sinsp_int.h" #include "../libscap/scap.h" #include "dumper.h" sinsp_dumper::sinsp_dumper(sinsp* inspector) { m_inspector = inspector; m_dumper = NULL; m_target_memory_buffer = NULL; m_target_memory_buffer_size = 0; m_nevts = 0; } sinsp_dumper::sinsp_dumper(sinsp* inspector, uint8_t* target_memory_buffer, uint64_t target_memory_buffer_size) { m_inspector = inspector; m_dumper = NULL; m_target_memory_buffer = target_memory_buffer; m_target_memory_buffer_size = target_memory_buffer_size; } sinsp_dumper::~sinsp_dumper() { if(m_dumper != NULL) { scap_dump_close(m_dumper); } } void sinsp_dumper::open(const string& filename, bool compress, bool threads_from_sinsp) { if(m_inspector->m_h == NULL) { throw sinsp_exception("can't start event dump, inspector not opened yet"); } if(m_target_memory_buffer) { m_dumper = scap_memory_dump_open(m_inspector->m_h, m_target_memory_buffer, m_target_memory_buffer_size); } else { if(compress) { m_dumper = scap_dump_open(m_inspector->m_h, filename.c_str(), SCAP_COMPRESSION_GZIP, threads_from_sinsp); } else { m_dumper = scap_dump_open(m_inspector->m_h, filename.c_str(), SCAP_COMPRESSION_NONE, threads_from_sinsp); } } if(m_dumper == NULL) { throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); } if(threads_from_sinsp) { m_inspector->m_thread_manager->dump_threads_to_file(m_dumper); } m_inspector->m_container_manager.dump_containers(m_dumper); m_nevts = 0; } void sinsp_dumper::fdopen(int fd, bool compress, bool threads_from_sinsp) { if(m_inspector->m_h == NULL) { throw sinsp_exception("can't start event dump, inspector not opened yet"); } if(compress) { m_dumper = scap_dump_open_fd(m_inspector->m_h, fd, SCAP_COMPRESSION_GZIP, threads_from_sinsp); } else { m_dumper = scap_dump_open_fd(m_inspector->m_h, fd, SCAP_COMPRESSION_NONE, threads_from_sinsp); } if(m_dumper == NULL) { throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); } if(threads_from_sinsp) { m_inspector->m_thread_manager->dump_threads_to_file(m_dumper); } m_inspector->m_container_manager.dump_containers(m_dumper); m_nevts = 0; } void sinsp_dumper::close() { if(m_dumper != NULL) { scap_dump_close(m_dumper); m_dumper = NULL; } } bool sinsp_dumper::is_open() { return (m_dumper != NULL); } bool sinsp_dumper::written_events() { return m_nevts; } void sinsp_dumper::dump(sinsp_evt* evt) { if(m_dumper == NULL) { throw sinsp_exception("dumper not opened yet"); } scap_evt* pdevt = (evt->m_poriginal_evt)? evt->m_poriginal_evt : evt->m_pevt; int32_t res = scap_dump(m_inspector->m_h, m_dumper, pdevt, evt->m_cpuid, 0); if(res != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); } m_nevts++; } uint64_t sinsp_dumper::written_bytes() { if(m_dumper == NULL) { return 0; } int64_t written_bytes = scap_dump_get_offset(m_dumper); if(written_bytes == -1) { throw sinsp_exception("error getting offset"); } return written_bytes; } uint64_t sinsp_dumper::next_write_position() { if(m_dumper == NULL) { return 0; } int64_t position = scap_dump_ftell(m_dumper); if(position == -1) { throw sinsp_exception("error getting offset"); } return position; } void sinsp_dumper::flush() { if(m_dumper == NULL) { throw sinsp_exception("dumper not opened yet"); } scap_dump_flush(m_dumper); } sysdig-0.26.4/userspace/libsinsp/dumper.h000066400000000000000000000061221352731327100203740ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once class sinsp; class sinsp_evt; /** @defgroup dump Dumping events to disk * Classes to perform miscellaneous functionality * @{ */ /*! \brief A support class to dump events to file in scap format. */ class SINSP_PUBLIC sinsp_dumper { public: /*! \brief Constructs the dumper. \param inspector Pointer to the inspector object that will be the source of the events to save. */ sinsp_dumper(sinsp* inspector); /*! \brief Constructs a dumper that saves to memory instead of disk. Takes the address and the size of a preallocated memory buffer where the data will go. */ sinsp_dumper(sinsp* inspector, uint8_t* target_memory_buffer, uint64_t target_memory_buffer_size); ~sinsp_dumper(); /*! \brief Opens the dump file. \param filename The name of the target file. \param compress true to save the trace file in a compressed format. \param threads_from_sinsp If, true the thread and FD tables in the file will be created from the current sinsp's tables instead of reusing the scap ones. \note There's no close() because the file is closed when the dumper is destroyed. */ void open(const string& filename, bool compress, bool threads_from_sinsp=false); void fdopen(int fd, bool compress, bool threads_from_sinsp=false); /*! \brief Closes the dump file. */ void close(); /*! \brief Return whether or not the underling scap file has been opened. */ bool is_open(); /*! \brief Return the number of events dumped so far. */ bool written_events(); /*! \brief Return the current size of a trace file. \return The current size of the dump file. */ uint64_t written_bytes(); /*! \brief Return the starting position for the next write into the file. (Under the covers, this uses gztell while written_bytes uses gzoffset, which represent different values). \return The starting position for the next write. */ uint64_t next_write_position(); /*! \brief Flush all pending output into the file. */ void flush(); /*! \brief Writes an event to the file. \param evt Pointer to the event to dump. */ void dump(sinsp_evt* evt); inline uint8_t* get_memory_dump_cur_buf() { return scap_get_memorydumper_curpos(m_dumper); } inline void set_inspector(sinsp *inspector) { m_inspector = inspector; } private: sinsp* m_inspector; scap_dumper_t* m_dumper; uint8_t* m_target_memory_buffer; uint64_t m_target_memory_buffer_size; uint64_t m_nevts; }; /*@}*/ sysdig-0.26.4/userspace/libsinsp/event.cpp000066400000000000000000001557751352731327100205770ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #ifndef _WIN32 #define __STDC_FORMAT_MACROS #include #include #include #else #define NOMINMAX #endif #include #include "sinsp.h" #include "sinsp_int.h" #include "../libscap/scap.h" extern sinsp_evttables g_infotables; #define SET_NUMERIC_FORMAT(resfmt, fmt, ostr, ustr, xstr) do { \ if(fmt == ppm_print_format::PF_OCT) \ { \ resfmt = (char*)"%#" ostr; \ } \ else if(fmt == ppm_print_format::PF_DEC) \ { \ resfmt = (char*)"%" ustr; \ } \ else if(fmt == ppm_print_format::PF_10_PADDED_DEC) \ { \ resfmt = (char*)"%09" ustr; \ } \ else if(fmt == ppm_print_format::PF_HEX) \ { \ resfmt = (char*)"%" xstr; \ } \ else \ { \ resfmt = (char*)"%" ustr; \ } \ } while(0) /////////////////////////////////////////////////////////////////////////////// // sinsp_evt implementation /////////////////////////////////////////////////////////////////////////////// sinsp_evt::sinsp_evt() : m_pevt_storage(NULL), m_paramstr_storage(256), m_resolved_paramstr_storage(1024) { m_flags = EF_NONE; m_tinfo = NULL; #ifdef _DEBUG m_filtered_out = false; #endif m_event_info_table = g_infotables.m_event_info; } sinsp_evt::sinsp_evt(sinsp *inspector) : m_pevt_storage(NULL), m_paramstr_storage(1024), m_resolved_paramstr_storage(1024) { m_inspector = inspector; m_flags = EF_NONE; m_tinfo = NULL; #ifdef _DEBUG m_filtered_out = false; #endif m_event_info_table = g_infotables.m_event_info; } sinsp_evt::~sinsp_evt() { if(m_pevt_storage) { delete[] m_pevt_storage; } } uint32_t sinsp_evt::get_dump_flags() { return scap_event_get_dump_flags(m_inspector->m_h); } const char *sinsp_evt::get_name() { return m_info->name; } event_direction sinsp_evt::get_direction() { return (event_direction)(m_pevt->type & PPME_DIRECTION_FLAG); } int64_t sinsp_evt::get_tid() { return m_pevt->tid; } void sinsp_evt::set_iosize(uint32_t size) { m_iosize = size; } uint32_t sinsp_evt::get_iosize() { return m_iosize; } sinsp_threadinfo* sinsp_evt::get_thread_info(bool query_os_if_not_found) { if(NULL != m_tinfo) { return m_tinfo; } else if(m_tinfo_ref) { m_tinfo = m_tinfo_ref.get(); return m_tinfo; } return m_inspector->get_thread(m_pevt->tid, query_os_if_not_found, false); } int64_t sinsp_evt::get_fd_num() { if(m_fdinfo) { return m_tinfo->m_lastevent_fd; } else { return sinsp_evt::INVALID_FD_NUM; } } uint32_t sinsp_evt::get_num_params() { if((m_flags & sinsp_evt::SINSP_EF_PARAMS_LOADED) == 0) { load_params(); m_flags |= (uint32_t)sinsp_evt::SINSP_EF_PARAMS_LOADED; } return (uint32_t)m_params.size(); } sinsp_evt_param *sinsp_evt::get_param(uint32_t id) { if((m_flags & sinsp_evt::SINSP_EF_PARAMS_LOADED) == 0) { load_params(); m_flags |= (uint32_t)sinsp_evt::SINSP_EF_PARAMS_LOADED; } return &(m_params[id]); } const char *sinsp_evt::get_param_name(uint32_t id) { if((m_flags & sinsp_evt::SINSP_EF_PARAMS_LOADED) == 0) { load_params(); m_flags |= (uint32_t)sinsp_evt::SINSP_EF_PARAMS_LOADED; } ASSERT(id < m_info->nparams); return m_info->params[id].name; } const struct ppm_param_info* sinsp_evt::get_param_info(uint32_t id) { if((m_flags & sinsp_evt::SINSP_EF_PARAMS_LOADED) == 0) { load_params(); m_flags |= (uint32_t)sinsp_evt::SINSP_EF_PARAMS_LOADED; } ASSERT(id < m_info->nparams); return &(m_info->params[id]); } uint32_t binary_buffer_to_hex_string(char *dst, char *src, uint32_t dstlen, uint32_t srclen, sinsp_evt::param_fmt fmt) { uint32_t j; uint32_t k; uint32_t l = 0; uint32_t num_chunks; uint32_t row_len; char row[128]; char *ptr; bool truncated = false; for(j = 0; j < srclen; j += 8 * sizeof(uint16_t)) { k = 0; k += sprintf(row + k, "\n\t0x%.4x:", j); ptr = &src[j]; num_chunks = 0; while(num_chunks < 8 && ptr < src + srclen) { uint16_t chunk = htons(*(uint16_t*)ptr); if(ptr == src + srclen - 1) { k += sprintf(row + k, " %.2x", *(((uint8_t*)&chunk) + 1)); } else { k += sprintf(row + k, " %.4x", chunk); } num_chunks++; ptr += sizeof(uint16_t); } if((fmt & sinsp_evt::PF_HEXASCII) || (fmt & sinsp_evt::PF_JSONHEXASCII)) { // Fill the row with spaces to align it to other rows while(num_chunks < 8) { memset(row + k, ' ', 5); k += 5; num_chunks++; } row[k++] = ' '; row[k++] = ' '; for(ptr = &src[j]; ptr < src + j + 8 * sizeof(uint16_t) && ptr < src + srclen; ptr++, k++) { if(isprint((int)(uint8_t)*ptr)) { row[k] = *ptr; } else { row[k] = '.'; } } } row[k] = 0; row_len = (uint32_t)strlen(row); if(l + row_len >= dstlen - 1) { truncated = true; break; } strcpy(dst + l, row); l += row_len; } dst[l++] = '\n'; if(truncated) { return dstlen; } else { return l; } } uint32_t binary_buffer_to_asciionly_string(char *dst, char *src, uint32_t dstlen, uint32_t srclen, sinsp_evt::param_fmt fmt) { uint32_t j; uint32_t k = 0; if(fmt != sinsp_evt::PF_EOLS_COMPACT) { dst[k++] = '\n'; } for(j = 0; j < srclen; j++) { // // Make sure there's enough space in the target buffer. // Note that we reserve two bytes, because some characters are expanded // when copied. // if(k >= dstlen - 1) { dst[k - 1] = 0; return dstlen; } if(isprint((int)(uint8_t)src[j])) { // switch(src[j]) // { // case '"': // case '\\': // dst[k++] = '\\'; // break; // default: // break; // } dst[k] = src[j]; k++; } else if(src[j] == '\r') { dst[k] = '\n'; k++; } else if(src[j] == '\n') { if(j > 0 && src[j - 1] != '\r') { dst[k] = src[j]; k++; } } } return k; } uint32_t binary_buffer_to_string_dots(char *dst, char *src, uint32_t dstlen, uint32_t srclen, sinsp_evt::param_fmt fmt) { uint32_t j; uint32_t k = 0; for(j = 0; j < srclen; j++) { // // Make sure there's enough space in the target buffer. // Note that we reserve two bytes, because some characters are expanded // when copied. // if(k >= dstlen - 1) { dst[k - 1] = 0; return dstlen; } if(isprint((int)(uint8_t)src[j])) { // switch(src[j]) // { // case '"': // case '\\': // dst[k++] = '\\'; // break; // default: // break; // } dst[k] = src[j]; } else { dst[k] = '.'; } k++; } return k; } uint32_t binary_buffer_to_base64_string(char *dst, char *src, uint32_t dstlen, uint32_t srclen, sinsp_evt::param_fmt fmt) { // // base64 encoder, malloc-free version of: // http://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c // static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; static uint32_t mod_table[] = {0, 2, 1}; uint32_t j,k, enc_dstlen; enc_dstlen = 4 * ((srclen + 2) / 3); // // Make sure there's enough space in the target buffer. // if(enc_dstlen >= dstlen - 1) { return dstlen; } for (j = 0, k = 0; j < srclen;) { uint32_t octet_a = j < srclen ? (unsigned char)src[j++] : 0; uint32_t octet_b = j < srclen ? (unsigned char)src[j++] : 0; uint32_t octet_c = j < srclen ? (unsigned char)src[j++] : 0; uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; dst[k++] = encoding_table[(triple >> 3 * 6) & 0x3F]; dst[k++] = encoding_table[(triple >> 2 * 6) & 0x3F]; dst[k++] = encoding_table[(triple >> 1 * 6) & 0x3F]; dst[k++] = encoding_table[(triple >> 0 * 6) & 0x3F]; } for (j = 0; j < mod_table[srclen % 3]; j++) dst[enc_dstlen - 1 - j] = '='; return enc_dstlen; } uint32_t binary_buffer_to_json_string(char *dst, char *src, uint32_t dstlen, uint32_t srclen, sinsp_evt::param_fmt fmt) { uint32_t k = 0; switch(fmt) { case sinsp_evt::PF_JSONHEX: case sinsp_evt::PF_JSONHEXASCII: k = binary_buffer_to_hex_string(dst, src, dstlen, srclen, fmt); break; case sinsp_evt::PF_JSONEOLS: k = binary_buffer_to_asciionly_string(dst, src, dstlen, srclen, fmt); break; case sinsp_evt::PF_JSONBASE64: k = binary_buffer_to_base64_string(dst, src, dstlen, srclen, fmt); break; default: k = binary_buffer_to_string_dots(dst, src, dstlen, srclen, fmt); } return k; } uint32_t binary_buffer_to_string(char *dst, char *src, uint32_t dstlen, uint32_t srclen, sinsp_evt::param_fmt fmt) { uint32_t k = 0; if(dstlen == 0) { ASSERT(false); return 0; } if(srclen == 0) { *dst = 0; return 0; } if(fmt & sinsp_evt::PF_HEX || fmt & sinsp_evt::PF_HEXASCII) { k = binary_buffer_to_hex_string(dst, src, dstlen, srclen, fmt); } else if(fmt & sinsp_evt::PF_BASE64) { k = binary_buffer_to_base64_string(dst, src, dstlen, srclen, fmt); } else if(fmt & sinsp_evt::PF_JSON || fmt & sinsp_evt::PF_JSONHEX || fmt & sinsp_evt::PF_JSONEOLS || fmt & sinsp_evt::PF_JSONHEXASCII || fmt & sinsp_evt::PF_JSONBASE64) { k = binary_buffer_to_json_string(dst, src, dstlen, srclen, fmt); } else if(fmt & (sinsp_evt::PF_EOLS | sinsp_evt::PF_EOLS_COMPACT)) { k = binary_buffer_to_asciionly_string(dst, src, dstlen, srclen, fmt); } else { k = binary_buffer_to_string_dots(dst, src, dstlen, srclen, fmt); } dst[k] = 0; return k; } uint32_t strcpy_sanitized(char *dest, char *src, uint32_t dstsize) { volatile char* tmp = (volatile char *)dest; uint32_t j = 0; g_invalidchar ic; while(j < dstsize) { if(!ic(*src)) { *tmp = *src; tmp++; j++; } if(*src == 0) { *tmp = 0; return j + 1; } src++; } // // In case there wasn't enough space, null-terminate the destination // if(dstsize) { dest[dstsize - 1] = 0; } return dstsize; } int sinsp_evt::render_fd_json(Json::Value *ret, int64_t fd, const char** resolved_str, sinsp_evt::param_fmt fmt) { sinsp_threadinfo* tinfo = get_thread_info(); if(tinfo == NULL) { return 0; } if(fd >= 0) { sinsp_fdinfo_t *fdinfo = tinfo->get_fd(fd); if(fdinfo) { char tch = fdinfo->get_typechar(); char ipprotoch = 0; if(fdinfo->m_type == SCAP_FD_IPV4_SOCK || fdinfo->m_type == SCAP_FD_IPV6_SOCK || fdinfo->m_type == SCAP_FD_IPV4_SERVSOCK || fdinfo->m_type == SCAP_FD_IPV6_SERVSOCK) { scap_l4_proto l4p = fdinfo->get_l4proto(); switch(l4p) { case SCAP_L4_TCP: ipprotoch = 't'; break; case SCAP_L4_UDP: ipprotoch = 'u'; break; case SCAP_L4_ICMP: ipprotoch = 'i'; break; case SCAP_L4_RAW: ipprotoch = 'r'; break; default: break; } } char typestr[3] = { (fmt & PF_SIMPLE)?(char)0:tch, ipprotoch, 0 }; // // Make sure we remove invalid characters from the resolved name // string sanitized_str = fdinfo->m_name; sanitize_string(sanitized_str); (*ret)["typechar"] = typestr; (*ret)["name"] = sanitized_str; } } else if(fd == PPM_AT_FDCWD) { // // `fd` can be AT_FDCWD on all *at syscalls // (*ret)["name"] = "AT_FDCWD"; } else { // // Resolve this as an errno // string errstr(sinsp_utils::errno_to_str((int32_t)fd)); if(errstr != "") { (*ret)["error"] = errstr; return 0; } } return 1; } char* sinsp_evt::render_fd(int64_t fd, const char** resolved_str, sinsp_evt::param_fmt fmt) { // // Add the fd number // snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%" PRId64, fd); sinsp_threadinfo* tinfo = get_thread_info(); if(tinfo == NULL) { // // no thread. Definitely can't resolve the fd, just return the number // return &m_paramstr_storage[0]; } if(fd >= 0) { sinsp_fdinfo_t *fdinfo = tinfo->get_fd(fd); if(fdinfo) { char tch = fdinfo->get_typechar(); char ipprotoch = 0; if(fdinfo->m_type == SCAP_FD_IPV4_SOCK || fdinfo->m_type == SCAP_FD_IPV6_SOCK || fdinfo->m_type == SCAP_FD_IPV4_SERVSOCK || fdinfo->m_type == SCAP_FD_IPV6_SERVSOCK) { scap_l4_proto l4p = fdinfo->get_l4proto(); switch(l4p) { case SCAP_L4_TCP: ipprotoch = 't'; break; case SCAP_L4_UDP: ipprotoch = 'u'; break; case SCAP_L4_ICMP: ipprotoch = 'i'; break; case SCAP_L4_RAW: ipprotoch = 'r'; break; default: break; } } char typestr[3] = { (fmt & PF_SIMPLE)?(char)0:tch, ipprotoch, 0 }; // // Make sure we remove invalid characters from the resolved name // string sanitized_str = fdinfo->m_name; sanitize_string(sanitized_str); // // Make sure the string will fit // if(sanitized_str.size() >= m_resolved_paramstr_storage.size()) { m_resolved_paramstr_storage.resize(sanitized_str.size() + 1); } snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "<%s>%s", typestr, sanitized_str.c_str()); /* XXX if(sanitized_str.length() == 0) { snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "<%c>", tch); } else { snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "%s", sanitized_str.c_str()); } */ } } else if(fd == PPM_AT_FDCWD) { // // `fd` can be AT_FDCWD on all *at syscalls // snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "AT_FDCWD"); } else { // // Resolve this as an errno // string errstr(sinsp_utils::errno_to_str((int32_t)fd)); if(errstr != "") { snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "%s", errstr.c_str()); } } return &m_paramstr_storage[0]; } Json::Value sinsp_evt::get_param_as_json(uint32_t id, OUT const char** resolved_str, sinsp_evt::param_fmt fmt) { const ppm_param_info* param_info; char* payload; uint16_t payload_len; Json::Value ret; // // Make sure the params are actually loaded // if((m_flags & sinsp_evt::SINSP_EF_PARAMS_LOADED) == 0) { load_params(); m_flags |= (uint32_t)sinsp_evt::SINSP_EF_PARAMS_LOADED; } ASSERT(id < get_num_params()); // // Reset the resolved string // m_resolved_paramstr_storage[0] = 0; // // Get the parameter // sinsp_evt_param *param = &(m_params[id]); payload = param->m_val; payload_len = param->m_len; param_info = &(m_info->params[id]); // // Get the parameter information // if(param_info->type == PT_DYN && param_info->info != NULL && payload_len != 0) { uint8_t dyn_idx = *(uint8_t*)payload; if(dyn_idx < param_info->ninfo) { const struct ppm_param_info* dyn_params = (const struct ppm_param_info*)param_info->info; payload += sizeof(uint8_t); payload_len -= sizeof(uint8_t); param_info = &dyn_params[dyn_idx]; } } switch(param_info->type) { case PT_INT8: ASSERT(payload_len == sizeof(int8_t)); ret = *(int8_t *)payload; break; case PT_INT16: ASSERT(payload_len == sizeof(int16_t)); ret = *(int16_t *)payload; break; case PT_INT32: ASSERT(payload_len == sizeof(int32_t)); ret = *(int32_t *)payload; break; case PT_INT64: ASSERT(payload_len == sizeof(int64_t)); ret = (Json::Value::Int64)*(int64_t *)payload; break; case PT_UINT8: ASSERT(payload_len == sizeof(uint8_t)); ret = *(uint8_t *)payload; break; case PT_UINT16: ASSERT(payload_len == sizeof(uint16_t)); ret = *(uint16_t *)payload; break; case PT_UINT32: ASSERT(payload_len == sizeof(uint32_t)); ret = *(uint32_t *)payload; break; case PT_UINT64: ASSERT(payload_len == sizeof(uint64_t)); ret = (Json::Value::UInt64)*(int64_t *)payload; break; case PT_PID: { ASSERT(payload_len == sizeof(int64_t)); ret = (Json::Value::UInt64)*(int64_t *)payload; sinsp_threadinfo* atinfo = m_inspector->get_thread(*(int64_t *)payload, false, true); if(atinfo != NULL) { string& tcomm = atinfo->m_comm; // // Make sure the string will fit // if(tcomm.size() >= m_resolved_paramstr_storage.size()) { m_resolved_paramstr_storage.resize(tcomm.size() + 1); } snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "%s", tcomm.c_str()); } } break; case PT_ERRNO: { ASSERT(payload_len == sizeof(int64_t)); int64_t val = *(int64_t *)payload; // // Resolve this as an errno // string errstr; if(val < 0) { errstr = sinsp_utils::errno_to_str((int32_t)val); if(errstr != "") { snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "%s", errstr.c_str()); } } ret = (Json::Value::Int64)val; } break; case PT_FD: { // We use the string extractor to get // the resolved path, but use our routine // to get the actual value to return ASSERT(payload_len == sizeof(int64_t)); int64_t fd = *(int64_t*)payload; render_fd_json(&ret, fd, resolved_str, fmt); ret["num"] = (Json::Value::UInt64)*(int64_t *)payload; break; } case PT_CHARBUF: case PT_FSPATH: case PT_BYTEBUF: ret = get_param_as_str(id, resolved_str, fmt); break; case PT_SOCKADDR: if(payload_len == 0) { ret = Json::nullValue; break; } else if(payload[0] == AF_UNIX) { ASSERT(payload_len > 1); // // Sanitize the file string. // string sanitized_str = payload + 1; sanitize_string(sanitized_str); ret = sanitized_str; } else if(payload[0] == PPM_AF_INET) { if(payload_len == 1 + 4 + 2) { const int ipv4_len = (3 + 1) * 4 + 1; char ipv4_addr[ ipv4_len ]; snprintf( ipv4_addr, ipv4_len, "%u.%u.%u.%u", (unsigned int)(uint8_t)payload[1], (unsigned int)(uint8_t)payload[2], (unsigned int)(uint8_t)payload[3], (unsigned int)(uint8_t)payload[4] ); ret["addr"] = string(ipv4_addr); ret["port"] = *(uint16_t*)(payload + 5); } else { ASSERT(false); ret = "INVALID IPv4"; } } else { ret["family"] = (int)payload[0]; } break; case PT_SOCKTUPLE: if(payload_len == 0) { ret = Json::nullValue; break; } if(payload[0] == PPM_AF_INET) { if(payload_len == 1 + 4 + 2 + 4 + 2) { Json::Value source; Json::Value dest; const int ipv4_len = (3 + 1) * 4 + 1; char ipv4_addr[ ipv4_len ]; snprintf( ipv4_addr, ipv4_len, "%u.%u.%u.%u", (unsigned int)(uint8_t)payload[1], (unsigned int)(uint8_t)payload[2], (unsigned int)(uint8_t)payload[3], (unsigned int)(uint8_t)payload[4] ); source["addr"] = string(ipv4_addr); source["port"] = *(uint16_t*)(payload + 5); snprintf( ipv4_addr, ipv4_len, "%u.%u.%u.%u", (unsigned int)(uint8_t)payload[7], (unsigned int)(uint8_t)payload[8], (unsigned int)(uint8_t)payload[9], (unsigned int)(uint8_t)payload[10] ); dest["addr"] = string(ipv4_addr); dest["port"] = *(uint16_t*)(payload + 11); ret["src"] = source; ret["dst"] = dest; } else { ASSERT(false); ret = "INVALID IPv4"; } } else if(payload[0] == PPM_AF_INET6) { if(payload_len == 1 + 16 + 2 + 16 + 2) { uint8_t* sip6 = (uint8_t*)payload + 1; uint8_t* dip6 = (uint8_t*)payload + 19; uint8_t* sip = (uint8_t*)payload + 13; uint8_t* dip = (uint8_t*)payload + 31; if(sinsp_utils::is_ipv4_mapped_ipv6(sip6) && sinsp_utils::is_ipv4_mapped_ipv6(dip6)) { Json::Value source; Json::Value dest; const int ipv4_len = (3 + 1) * 4 + 1; char ipv4_addr[ ipv4_len ]; snprintf( ipv4_addr, ipv4_len, "%u.%u.%u.%u", (unsigned int)sip[0], (unsigned int)sip[1], (unsigned int)sip[2], (unsigned int)sip[3] ); source["addr"] = string(ipv4_addr); source["port"] = (unsigned int)*(uint16_t*)(payload + 17); snprintf( ipv4_addr, ipv4_len, "%u.%u.%u.%u", (unsigned int)dip[0], (unsigned int)dip[1], (unsigned int)dip[2], (unsigned int)dip[3] ); dest["addr"] = string(ipv4_addr); dest["port"] = (unsigned int)*(uint16_t*)(payload + 35); ret["src"] = source; ret["dst"] = dest; break; } else { char srcstr[INET6_ADDRSTRLEN]; char dststr[INET6_ADDRSTRLEN]; if(inet_ntop(AF_INET6, sip6, srcstr, sizeof(srcstr)) && inet_ntop(AF_INET6, dip6, dststr, sizeof(dststr))) { Json::Value source; Json::Value dest; source["addr"] = srcstr; source["port"] = (unsigned int)*(uint16_t*)(payload + 17); dest["addr"] = dststr; dest["port"] = (unsigned int)*(uint16_t*)(payload + 35); ret["src"] = source; ret["dst"] = dest; break; } } } ASSERT(false); ret = "INVALID IPv6"; } else if(payload[0] == AF_UNIX) { ASSERT(payload_len > 17); // // Sanitize the file string. // string sanitized_str = payload + 17; sanitize_string(sanitized_str); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%" PRIx64 "->%" PRIx64 " %s", *(uint64_t*)(payload + 1), *(uint64_t*)(payload + 9), sanitized_str.c_str()); } else { ret["family"] = (int)payload[0]; } break; case PT_FDLIST: ret = get_param_as_str(id, resolved_str, fmt); break; case PT_SYSCALLID: { uint16_t scid = *(uint16_t *)payload; if(scid >= PPM_SC_MAX) { ASSERT(false); snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), ""); break; } const struct ppm_syscall_desc* desc = &(g_infotables.m_syscall_info_table[scid]); ret = scid; snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "%s", desc->name); } break; case PT_SIGTYPE: { const char* sigstr; ASSERT(payload_len == sizeof(uint8_t)); uint8_t val = *(uint8_t *)payload; sigstr = sinsp_utils::signal_to_str(val); ret = val; if(sigstr) { snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "%s", sigstr); } } break; case PT_RELTIME: { ASSERT(payload_len == sizeof(uint64_t)); uint64_t val = *(uint64_t *)payload; ret = (Json::Value::Int64)val; snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "%lgs", ((double)val) / 1000000000); } break; case PT_FLAGS8: case PT_FLAGS16: case PT_FLAGS32: { uint32_t val = *(uint32_t *)payload & (((uint64_t)1 << payload_len * 8) - 1); ret["val"] = val; ret["flags"] = Json::arrayValue; const struct ppm_name_value *flags = (const struct ppm_name_value *)m_info->params[id].info; uint32_t initial_val = val; while(flags != NULL && flags->name != NULL && flags->value != initial_val) { // If flag is 0, then initial_val needs to be 0 for the flag to be resolved if((flags->value == 0 && initial_val == 0) || (flags->value != 0 && (val & flags->value) == flags->value && val != 0)) { ret["flags"].append(flags->name); // We remove current flags value to avoid duplicate flags e.g. PPM_O_RDWR, PPM_O_RDONLY, PPM_O_WRONLY val &= ~flags->value; } flags++; } if(flags != NULL && flags->name != NULL) { ret["flags"].append(flags->name); } break; } case PT_MODE: { uint32_t val = *(uint32_t *)payload & (((uint64_t)1 << payload_len * 8) - 1); ret["val"] = val; ret["mode"] = Json::arrayValue; const struct ppm_name_value *mode = (const struct ppm_name_value *)m_info->params[id].info; uint32_t initial_val = val; while(mode != NULL && mode->name != NULL && mode->value != initial_val) { // If mode is 0, then initial_val needs to be 0 for the mode to be resolved if((mode->value == 0 && initial_val == 0) || (mode->value != 0 && (val & mode->value) == mode->value && val != 0)) { ret["mode"].append(mode->name); // We remove current mode value to avoid duplicates val &= ~mode->value; } mode++; } if(mode != NULL && mode->name != NULL) { ret["mode"].append(mode->name); } break; } case PT_UID: case PT_GID: { ASSERT(payload_len == sizeof(uint32_t)); uint32_t val = *(uint32_t *)payload; if(val < std::numeric_limits::max() ) { ret = val; } else { ret = -1; } break; } case PT_CHARBUFARRAY: { ASSERT(param->m_len == sizeof(uint64_t)); vector* strvect = (vector*)*(uint64_t *)param->m_val; m_paramstr_storage[0] = 0; while(true) { vector::iterator it; vector::iterator itbeg; bool need_to_resize = false; // // Copy the arguments // char* dst = &m_paramstr_storage[0]; char* dstend = &m_paramstr_storage[0] + m_paramstr_storage.size() - 2; for(it = itbeg = strvect->begin(); it != strvect->end(); ++it) { char* src = *it; if(it != itbeg) { if(dst < dstend - 1) { *dst++ = ','; } } while(*src != 0 && dst < dstend) { *dst++ = *src++; } if(dst == dstend) { // // Reached the end of m_paramstr_storage, we need to resize it // need_to_resize = true; break; } } if(need_to_resize) { m_paramstr_storage.resize(m_paramstr_storage.size() * 2); continue; } *dst = 0; break; } } break; case PT_CHARBUF_PAIR_ARRAY: { ASSERT(param->m_len == sizeof(uint64_t)); pair*, vector*>* pairs = (pair*, vector*>*)*(uint64_t *)param->m_val; ASSERT(pairs->first->size() == pairs->second->size()); m_paramstr_storage[0] = 0; while(true) { vector::iterator it1; vector::iterator itbeg1; vector::iterator it2; vector::iterator itbeg2; bool need_to_resize = false; // // Copy the arguments // char* dst = &m_paramstr_storage[0]; char* dstend = &m_paramstr_storage[0] + m_paramstr_storage.size() - 2; for(it1 = itbeg1 = pairs->first->begin(), it2 = itbeg2 = pairs->second->begin(); it1 != pairs->first->end(); ++it1, ++it2) { char* src = *it1; if(it1 != itbeg1) { if(dst < dstend - 1) { *dst++ = ','; } } // // Copy the first string // while(*src != 0 && dst < dstend) { *dst++ = *src++; } if(dst < dstend - 1) { *dst++ = ':'; } // // Copy the second string // src = *it2; while(*src != 0 && dst < dstend) { *dst++ = *src++; } if(dst == dstend) { // // Reached the end of m_paramstr_storage, we need to resize it // need_to_resize = true; break; } } if(need_to_resize) { m_paramstr_storage.resize(m_paramstr_storage.size() * 2); continue; } *dst = 0; break; } } case PT_ABSTIME: // // XXX not implemented yet // ASSERT(false); case PT_DYN: ASSERT(false); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "INVALID DYNAMIC PARAMETER"); break; case PT_SIGSET: ret = get_param_as_str(id, resolved_str, fmt); break; default: ASSERT(false); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "(n.a.)"); break; } *resolved_str = &m_resolved_paramstr_storage[0]; return ret; } const char* sinsp_evt::get_param_as_str(uint32_t id, OUT const char** resolved_str, sinsp_evt::param_fmt fmt) { char* prfmt; const ppm_param_info* param_info; char* payload; uint32_t j; uint16_t payload_len; // // Make sure the params are actually loaded // if((m_flags & sinsp_evt::SINSP_EF_PARAMS_LOADED) == 0) { load_params(); m_flags |= (uint32_t)sinsp_evt::SINSP_EF_PARAMS_LOADED; } ASSERT(id < get_num_params()); // // Reset the resolved string // m_resolved_paramstr_storage[0] = 0; // // Get the parameter // sinsp_evt_param *param = &(m_params[id]); payload = param->m_val; payload_len = param->m_len; param_info = &(m_info->params[id]); // // Get the parameter information // if(param_info->type == PT_DYN && param_info->info != NULL && payload_len != 0) { uint8_t dyn_idx = *(uint8_t*)payload; if(dyn_idx < param_info->ninfo) { const struct ppm_param_info* dyn_params = (const struct ppm_param_info*)param_info->info; payload += sizeof(uint8_t); payload_len -= sizeof(uint8_t); param_info = &dyn_params[dyn_idx]; } } ppm_print_format param_fmt = m_info->params[id].fmt; switch(param_info->type) { case PT_INT8: ASSERT(payload_len == sizeof(int8_t)); SET_NUMERIC_FORMAT(prfmt, param_fmt, PRIo8, PRId8, PRIX8); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), prfmt, *(int8_t *)payload); break; case PT_INT16: ASSERT(payload_len == sizeof(int16_t)); SET_NUMERIC_FORMAT(prfmt, param_fmt, PRIo16, PRId16, PRIX16); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), prfmt, *(int16_t *)payload); break; case PT_INT32: ASSERT(payload_len == sizeof(int32_t)); SET_NUMERIC_FORMAT(prfmt, param_fmt, PRIo32, PRId32, PRIX32); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), prfmt, *(int32_t *)payload); break; case PT_INT64: ASSERT(payload_len == sizeof(int64_t)); SET_NUMERIC_FORMAT(prfmt, param_fmt, PRIo64, PRId64, PRIX64); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), prfmt, *(int64_t *)payload); break; case PT_FD: { ASSERT(payload_len == sizeof(int64_t)); int64_t fd = *(int64_t*)payload; render_fd(fd, resolved_str, fmt); break; } case PT_PID: { ASSERT(payload_len == sizeof(int64_t)); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%" PRId64, *(int64_t *)payload); sinsp_threadinfo* atinfo = m_inspector->get_thread(*(int64_t *)payload, false, true); if(atinfo != NULL) { string& tcomm = atinfo->m_comm; // // Make sure the string will fit // if(tcomm.size() >= m_resolved_paramstr_storage.size()) { m_resolved_paramstr_storage.resize(tcomm.size() + 1); } snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "%s", tcomm.c_str()); } } break; case PT_UINT8: ASSERT(payload_len == sizeof(uint8_t)); SET_NUMERIC_FORMAT(prfmt, param_fmt, PRIo8, PRId8, PRIX8); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), prfmt, *(uint8_t *)payload); break; case PT_UINT16: ASSERT(payload_len == sizeof(uint16_t)); SET_NUMERIC_FORMAT(prfmt, param_fmt, PRIo16, PRId16, PRIX16); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), prfmt, *(uint16_t *)payload); break; case PT_UINT32: ASSERT(payload_len == sizeof(uint32_t)); SET_NUMERIC_FORMAT(prfmt, param_fmt, PRIo32, PRId32, PRIX32); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), prfmt, *(uint32_t *)payload); break; case PT_ERRNO: { ASSERT(payload_len == sizeof(int64_t)); int64_t val = *(int64_t *)payload; snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%" PRId64, val); // // Resolve this as an errno // string errstr; if(val < 0) { errstr = sinsp_utils::errno_to_str((int32_t)val); if(errstr != "") { snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "%s", errstr.c_str()); } } } break; case PT_UINT64: ASSERT(payload_len == sizeof(uint64_t)); SET_NUMERIC_FORMAT(prfmt, param_fmt, PRIo64, PRId64, PRIX64); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), prfmt, *(int64_t *)payload); break; case PT_CHARBUF: // // Make sure the string will fit // if(payload_len > m_paramstr_storage.size()) { m_paramstr_storage.resize(payload_len); } snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%s", payload); break; case PT_FSPATH: { strcpy_sanitized(&m_paramstr_storage[0], payload, MIN(payload_len, (uint32_t)m_paramstr_storage.size())); sinsp_threadinfo* tinfo = get_thread_info(); if(tinfo) { if (strncmp(payload, "", 4) != 0) { string cwd = tinfo->get_cwd(); if(payload_len + cwd.length() >= m_resolved_paramstr_storage.size()) { m_resolved_paramstr_storage.resize(payload_len + cwd.length() + 1, 0); } if(!sinsp_utils::concatenate_paths(&m_resolved_paramstr_storage[0], (uint32_t)m_resolved_paramstr_storage.size(), (char*)cwd.c_str(), (uint32_t)cwd.length(), payload, payload_len)) { m_resolved_paramstr_storage[0] = 0; } } } else { *resolved_str = &m_paramstr_storage[0]; } } break; case PT_BYTEBUF: { /* This would include quotes around the output string m_paramstr_storage[0] = '"'; cres = binary_buffer_to_string(m_paramstr_storage + 1, param->m_val, m_paramstr_storage.size() - 2, param->m_len); m_paramstr_storage[cres + 1] = '"'; m_paramstr_storage[cres + 2] = 0; */ while(true) { uint32_t blen = binary_buffer_to_string(&m_paramstr_storage[0], payload, (uint32_t)m_paramstr_storage.size() - 1, payload_len, fmt); if(blen >= m_paramstr_storage.size() - 1) { // // The buffer didn't fit, expand it and try again // m_paramstr_storage.resize(m_paramstr_storage.size() * 2); continue; } ASSERT(m_inspector != NULL); if(m_inspector->m_max_evt_output_len != 0 && blen > m_inspector->m_max_evt_output_len && fmt == PF_NORMAL) { uint32_t real_len = MIN(blen, m_inspector->m_max_evt_output_len); m_rawbuf_str_len = real_len; if(real_len > 3) { m_paramstr_storage[real_len - 3] = '.'; m_paramstr_storage[real_len - 2] = '.'; m_paramstr_storage[real_len - 1] = '.'; } m_paramstr_storage[real_len] = 0; } else { m_rawbuf_str_len = blen; } break; } } break; case PT_SOCKADDR: if(payload_len == 0) { snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "NULL"); break; } else if(payload[0] == AF_UNIX) { ASSERT(payload_len > 1); // // Sanitize the file string. // string sanitized_str = payload + 1; sanitize_string(sanitized_str); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%s", sanitized_str.c_str()); } else if(payload[0] == PPM_AF_INET) { if(payload_len == 1 + 4 + 2) { ipv4serverinfo addr; addr.m_ip = *(uint32_t*)(payload + 1); addr.m_port = *(uint16_t*)(payload+5); addr.m_l4proto = (m_fdinfo != NULL) ? m_fdinfo->get_l4proto() : SCAP_L4_UNKNOWN; string straddr = ipv4serveraddr_to_string(&addr, m_inspector->m_hostname_and_port_resolution_enabled); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%s", straddr.c_str()); } else { ASSERT(false); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "INVALID IPv4"); } } else if(payload[0] == PPM_AF_INET6) { if(payload_len == 1 + 16 + 2) { ipv6serverinfo addr; memcpy((uint8_t *) addr.m_ip.m_b, (uint8_t *) payload+1, sizeof(addr.m_ip.m_b)); addr.m_port = *(uint16_t*)(payload+17); addr.m_l4proto = (m_fdinfo != NULL) ? m_fdinfo->get_l4proto() : SCAP_L4_UNKNOWN; string straddr = ipv6serveraddr_to_string(&addr, m_inspector->m_hostname_and_port_resolution_enabled); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%s", straddr.c_str()); } else { ASSERT(false); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "INVALID IPv6"); } } else { snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "family %d", (int)payload[0]); } break; case PT_SOCKTUPLE: if(payload_len == 0) { snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "NULL"); break; } if(payload[0] == PPM_AF_INET) { if(payload_len == 1 + 4 + 2 + 4 + 2) { ipv4tuple addr; addr.m_fields.m_sip = *(uint32_t*)(payload + 1); addr.m_fields.m_sport = *(uint16_t*)(payload+5); addr.m_fields.m_dip = *(uint32_t*)(payload + 7); addr.m_fields.m_dport = *(uint16_t*)(payload+11); addr.m_fields.m_l4proto = (m_fdinfo != NULL) ? m_fdinfo->get_l4proto() : SCAP_L4_UNKNOWN; string straddr = ipv4tuple_to_string(&addr, m_inspector->m_hostname_and_port_resolution_enabled); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%s", straddr.c_str()); } else { ASSERT(false); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "INVALID IPv4"); } } else if(payload[0] == PPM_AF_INET6) { if(payload_len == 1 + 16 + 2 + 16 + 2) { uint8_t* sip6 = (uint8_t*)payload + 1; uint8_t* dip6 = (uint8_t*)payload + 19; uint8_t* sip = (uint8_t*)payload + 13; uint8_t* dip = (uint8_t*)payload + 31; if(sinsp_utils::is_ipv4_mapped_ipv6(sip6) && sinsp_utils::is_ipv4_mapped_ipv6(dip6)) { ipv4tuple addr; addr.m_fields.m_sip = *(uint32_t*)sip; addr.m_fields.m_sport = *(uint16_t*)(payload+17); addr.m_fields.m_dip = *(uint32_t*)dip; addr.m_fields.m_dport = *(uint16_t*)(payload+35); addr.m_fields.m_l4proto = (m_fdinfo != NULL) ? m_fdinfo->get_l4proto() : SCAP_L4_UNKNOWN; string straddr = ipv4tuple_to_string(&addr, m_inspector->m_hostname_and_port_resolution_enabled); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%s", straddr.c_str()); break; } else { char srcstr[INET6_ADDRSTRLEN]; char dststr[INET6_ADDRSTRLEN]; if(inet_ntop(AF_INET6, sip6, srcstr, sizeof(srcstr)) && inet_ntop(AF_INET6, dip6, dststr, sizeof(dststr))) { snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%s:%s->%s:%s", srcstr, port_to_string(*(uint16_t*)(payload + 17), (m_fdinfo != NULL) ? m_fdinfo->get_l4proto() : SCAP_L4_UNKNOWN, m_inspector->m_hostname_and_port_resolution_enabled).c_str(), dststr, port_to_string(*(uint16_t*)(payload + 35), (m_fdinfo != NULL) ? m_fdinfo->get_l4proto() : SCAP_L4_UNKNOWN, m_inspector->m_hostname_and_port_resolution_enabled).c_str()); break; } } } ASSERT(false); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "INVALID IPv6"); } else if(payload[0] == AF_UNIX) { ASSERT(payload_len > 17); // // Sanitize the file string. // string sanitized_str = payload + 17; sanitize_string(sanitized_str); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%" PRIx64 "->%" PRIx64 " %s", *(uint64_t*)(payload + 1), *(uint64_t*)(payload + 9), sanitized_str.c_str()); } else { snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "family %d", (int)payload[0]); } break; case PT_FDLIST: { sinsp_threadinfo* tinfo = get_thread_info(); if(!tinfo) { break; } uint16_t nfds = *(uint16_t *)payload; uint32_t pos = 2; uint32_t spos = 0; m_paramstr_storage[0] = 0; for(j = 0; j < nfds; j++) { char tch; int64_t fd = *(int64_t *)(payload + pos); sinsp_fdinfo_t *fdinfo = tinfo->get_fd(fd); if(fdinfo) { tch = fdinfo->get_typechar(); } else { tch = '?'; } int r = snprintf(&m_paramstr_storage[0] + spos, m_paramstr_storage.size() - spos, "%" PRIu64 ":%c%x%c", fd, tch, (uint32_t) * (int16_t *)(payload + pos + 8), (j < (uint32_t)(nfds - 1)) ? ' ' : '\0'); if(r < 0 || spos + r >= m_paramstr_storage.size() - 1) { m_paramstr_storage[m_paramstr_storage.size() - 1] = 0; break; } spos += r; pos += 10; } } break; case PT_SYSCALLID: { uint16_t scid = *(uint16_t *)payload; if(scid >= PPM_SC_MAX) { ASSERT(false); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), ""); break; } const struct ppm_syscall_desc* desc = &(g_infotables.m_syscall_info_table[scid]); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%" PRIu16, scid); snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "%s", desc->name); } break; case PT_SIGTYPE: { const char* sigstr; ASSERT(payload_len == sizeof(uint8_t)); uint8_t val = *(uint8_t *)payload; sigstr = sinsp_utils::signal_to_str(val); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%" PRIu8, val); if(sigstr) { snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "%s", sigstr); } } break; case PT_RELTIME: { string sigstr; ASSERT(payload_len == sizeof(uint64_t)); uint64_t val = *(uint64_t *)payload; if(val == (uint64_t)(-1)) { snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "none"); m_resolved_paramstr_storage[0] = '\0'; } else { snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%" PRIu64, val); snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), "%lgs", ((double)val) / 1000000000); } } break; case PT_FLAGS8: case PT_FLAGS16: case PT_FLAGS32: { uint32_t val = (param_info->type == PT_FLAGS8) ? *(uint8_t *)payload : (param_info->type == PT_FLAGS16) ? *(uint16_t *)payload : *(uint32_t *)payload; snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%" PRIu32, val); const struct ppm_name_value *flags = (const struct ppm_name_value *)m_info->params[id].info; const char *separator = ""; uint32_t initial_val = val; uint32_t j = 0; while(flags != NULL && flags->name != NULL && flags->value != initial_val) { // If flag is 0, then initial_val needs to be 0 for the flag to be resolved if((flags->value == 0 && initial_val == 0) || (flags->value != 0 && (val & flags->value) == flags->value && val != 0)) { if(m_resolved_paramstr_storage.size() < j + strlen(separator) + strlen(flags->name)) { m_resolved_paramstr_storage.resize(m_resolved_paramstr_storage.size() * 2); } j += snprintf(&m_resolved_paramstr_storage[j], m_resolved_paramstr_storage.size(), "%s%s", separator, flags->name); separator = "|"; // We remove current flags value to avoid duplicate flags e.g. PPM_O_RDWR, PPM_O_RDONLY, PPM_O_WRONLY val &= ~flags->value; } flags++; } if(flags != NULL && flags->name != NULL) { j += snprintf(&m_resolved_paramstr_storage[j], m_resolved_paramstr_storage.size(), "%s%s", separator, flags->name); } break; } case PT_MODE: { uint32_t val = *(uint32_t *)payload; SET_NUMERIC_FORMAT(prfmt, param_fmt, PRIo32, PRId32, PRIX32); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), prfmt, val); const struct ppm_name_value *mode = (const struct ppm_name_value *)m_info->params[id].info; const char *separator = ""; uint32_t initial_val = val; uint32_t j = 0; while(mode != NULL && mode->name != NULL && mode->value != initial_val) { // If mode is 0, then initial_val needs to be 0 for the mode to be resolved if((mode->value == 0 && initial_val == 0) || (mode->value != 0 && (val & mode->value) == mode->value && val != 0)) { size_t params_len = j + strlen(separator) + strlen(mode->name); if(m_resolved_paramstr_storage.size() < params_len) { m_resolved_paramstr_storage.resize(params_len + 1); } j += snprintf(&m_resolved_paramstr_storage[j], m_resolved_paramstr_storage.size(), "%s%s", separator, mode->name); separator = "|"; // We remove current mode value to avoid duplicates val &= ~mode->value; } mode++; } if(mode != NULL && mode->name != NULL) { j += snprintf(&m_resolved_paramstr_storage[j], m_resolved_paramstr_storage.size(), "%s%s", separator, mode->name); } break; } case PT_ABSTIME: // // XXX not implemented yet // ASSERT(false); case PT_DYN: ASSERT(false); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "INVALID DYNAMIC PARAMETER"); break; case PT_UID: { uint32_t val = *(uint32_t *)payload; if (val < std::numeric_limits::max()) { snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%d", val); auto find_it = m_inspector->get_userlist()->find(val); if (find_it != m_inspector->get_userlist()->end()) { scap_userinfo* user_info = find_it->second; strcpy_sanitized(&m_resolved_paramstr_storage[0], user_info->name, (uint32_t)m_resolved_paramstr_storage.size()); } else { snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), ""); } } else { snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "-1"); snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), ""); } break; } case PT_GID: { uint32_t val = *(uint32_t *)payload; if (val < std::numeric_limits::max()) { snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%d", val); auto find_it = m_inspector->get_grouplist()->find(val); if (find_it != m_inspector->get_grouplist()->end()) { scap_groupinfo* group_info = find_it->second; strcpy_sanitized(&m_resolved_paramstr_storage[0], group_info->name, (uint32_t)m_resolved_paramstr_storage.size()); } else { snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), ""); } } else { snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "-1"); snprintf(&m_resolved_paramstr_storage[0], m_resolved_paramstr_storage.size(), ""); } break; } case PT_CHARBUFARRAY: { ASSERT(param->m_len == sizeof(uint64_t)); vector* strvect = (vector*)*(uint64_t *)param->m_val; m_paramstr_storage[0] = 0; while(true) { vector::iterator it; vector::iterator itbeg; bool need_to_resize = false; // // Copy the arguments // char* dst = &m_paramstr_storage[0]; char* dstend = &m_paramstr_storage[0] + m_paramstr_storage.size() - 2; for(it = itbeg = strvect->begin(); it != strvect->end(); ++it) { char* src = *it; if(it != itbeg) { if(dst < dstend - 1) { *dst++ = '.'; } } while(*src != 0 && dst < dstend) { *dst++ = *src++; } if(dst == dstend) { // // Reached the end of m_paramstr_storage, we need to resize it // need_to_resize = true; break; } } if(need_to_resize) { m_paramstr_storage.resize(m_paramstr_storage.size() * 2); continue; } *dst = 0; break; } } break; case PT_CHARBUF_PAIR_ARRAY: { ASSERT(param->m_len == sizeof(uint64_t)); pair*, vector*>* pairs = (pair*, vector*>*)*(uint64_t *)param->m_val; m_paramstr_storage[0] = 0; if(pairs->first->size() != pairs->second->size()) { ASSERT(false); break; } while(true) { vector::iterator it1; vector::iterator itbeg1; vector::iterator it2; vector::iterator itbeg2; bool need_to_resize = false; // // Copy the arguments // char* dst = &m_paramstr_storage[0]; char* dstend = &m_paramstr_storage[0] + m_paramstr_storage.size() - 2; for(it1 = itbeg1 = pairs->first->begin(), it2 = itbeg2 = pairs->second->begin(); it1 != pairs->first->end(); ++it1, ++it2) { char* src = *it1; if(it1 != itbeg1) { if(dst < dstend - 1) { *dst++ = ','; } } // // Copy the first string // while(*src != 0 && dst < dstend) { *dst++ = *src++; } if(dst < dstend - 1) { *dst++ = ':'; } // // Copy the second string // src = *it2; while(*src != 0 && dst < dstend) { *dst++ = *src++; } if(dst == dstend) { // // Reached the end of m_paramstr_storage, we need to resize it // need_to_resize = true; break; } } if(need_to_resize) { m_paramstr_storage.resize(m_paramstr_storage.size() * 2); continue; } *dst = 0; break; } break; } case PT_SIGSET: { ASSERT(payload_len == sizeof(uint32_t)); uint32_t val = *(uint32_t *)payload; m_resolved_paramstr_storage[0] = '\0'; m_paramstr_storage[0] = '\0'; char* storage = &m_paramstr_storage[0]; int remaining = (int)m_paramstr_storage.size(); bool first = true; for(int sig = 0; sig < 32; sig++) { if(val & (1U << sig) ) { const char* sigstr = sinsp_utils::signal_to_str(sig+1); if(sigstr) { int printed = snprintf(storage, remaining, "%s%s", !first ? " " : "", sigstr); if(printed >= remaining) { storage[remaining-1] = '\0'; break; } first = false; storage += printed; remaining -= printed; } } } break; } default: ASSERT(false); snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "(n.a.)"); break; } *resolved_str = &m_resolved_paramstr_storage[0]; return &m_paramstr_storage[0]; } string sinsp_evt::get_param_value_str(const string &name, bool resolved) { for(uint32_t i = 0; i < get_num_params(); i++) { if(name == get_param_name(i)) { return get_param_value_str(i, resolved); } } return string(""); } string sinsp_evt::get_param_value_str(const char *name, bool resolved) { // TODO fix this !! string s_name = string(name); return get_param_value_str(s_name, resolved); } string sinsp_evt::get_param_value_str(uint32_t i, bool resolved) { const char *param_value_str; const char *val_str; val_str = get_param_as_str(i, ¶m_value_str); if(resolved) { return string((*param_value_str == '\0')? val_str : param_value_str); } else { return string(val_str); } } const char* sinsp_evt::get_param_value_str(const char* name, OUT const char** resolved_str, param_fmt fmt) { for(uint32_t i = 0; i < get_num_params(); i++) { if(strcmp(name, get_param_name(i)) == 0) { return get_param_as_str(i, resolved_str, fmt); } } *resolved_str = NULL; return NULL; } const sinsp_evt_param* sinsp_evt::get_param_value_raw(const char* name) { // // Make sure the params are actually loaded // if((m_flags & sinsp_evt::SINSP_EF_PARAMS_LOADED) == 0) { load_params(); m_flags |= (uint32_t)sinsp_evt::SINSP_EF_PARAMS_LOADED; } // // Locate the parameter given the name // uint32_t np = get_num_params(); for(uint32_t j = 0; j < np; j++) { if(strcmp(name, get_param_name(j)) == 0) { return &(m_params[j]); } } return NULL; } void sinsp_evt::get_category(OUT sinsp_evt::category* cat) { if(m_pevt->type == PPME_GENERIC_E || m_pevt->type == PPME_GENERIC_X) { // // This event is a syscall that doesn't have a filler yet. // The category can be found in g_syscall_info_table. // sinsp_evt_param *parinfo = get_param(0); ASSERT(parinfo->m_len == sizeof(uint16_t)); uint16_t id = *(uint16_t *)parinfo->m_val; if(id < PPM_SC_MAX) { cat->m_category = g_infotables.m_syscall_info_table[id].category; cat->m_subcategory = sinsp_evt::SC_NONE; } else { ASSERT(false); } } else { // // This event has a real filler. // The category can be found in the info struct. // cat->m_category = m_info->category; // // For EC_IO and EC_WAIT events, we dig into the fd state to get the category // and fdtype // if(cat->m_category & EC_IO_BASE) { if(!m_fdinfo) { // // The fd info is not present, likely because we missed its creation. // cat->m_subcategory = SC_UNKNOWN; return; } else { switch(m_fdinfo->m_type) { case SCAP_FD_FILE: case SCAP_FD_FILE_V2: case SCAP_FD_DIRECTORY: cat->m_subcategory = SC_FILE; break; case SCAP_FD_IPV4_SOCK: case SCAP_FD_IPV6_SOCK: cat->m_subcategory = SC_NET; break; case SCAP_FD_IPV4_SERVSOCK: case SCAP_FD_IPV6_SERVSOCK: cat->m_subcategory = SC_NET; break; case SCAP_FD_FIFO: case SCAP_FD_UNIX_SOCK: case SCAP_FD_EVENT: case SCAP_FD_SIGNALFD: case SCAP_FD_INOTIFY: cat->m_subcategory = SC_IPC; break; case SCAP_FD_UNSUPPORTED: case SCAP_FD_EVENTPOLL: case SCAP_FD_TIMERFD: case SCAP_FD_NETLINK: cat->m_subcategory = SC_OTHER; break; case SCAP_FD_UNKNOWN: cat->m_subcategory = SC_OTHER; break; default: // ASSERT(false); cat->m_subcategory = SC_UNKNOWN; break; } } } else { cat->m_subcategory = sinsp_evt::SC_NONE; } } } bool sinsp_evt::is_filtered_out() { return m_filtered_out; } #ifdef HAS_FILTERING scap_dump_flags sinsp_evt::get_dump_flags(OUT bool* should_drop) { uint32_t dflags = SCAP_DF_NONE; *should_drop = false; if(m_filtered_out) { if(m_inspector->m_isfatfile_enabled) { ppm_event_flags eflags = get_info_flags(); if(eflags & EF_MODIFIES_STATE) { dflags = SCAP_DF_STATE_ONLY; } else { *should_drop = true; } } else { *should_drop = true; } if(*should_drop) { ppm_event_category ecat = get_info_category(); if(ecat & EC_INTERNAL) { *should_drop = false; } } } if(m_flags & sinsp_evt::SINSP_EF_IS_TRACER) { dflags |= SCAP_DF_TRACER; } return (scap_dump_flags)dflags; } #endif bool sinsp_evt::falco_consider() { uint16_t etype = get_type(); if(etype == PPME_GENERIC_E || etype == PPME_GENERIC_X) { sinsp_evt_param *parinfo = get_param(0); ASSERT(parinfo->m_len == sizeof(uint16_t)); uint16_t scid = *(uint16_t *)parinfo->m_val; return sinsp::falco_consider_syscallid(scid); } return sinsp::falco_consider_evtnum(etype); } sysdig-0.26.4/userspace/libsinsp/event.h000066400000000000000000000323611352731327100202250ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #ifndef VISIBILITY_PRIVATE #define VISIBILITY_PRIVATE private: #endif #include "sinsp_inet.h" #include "sinsp_public.h" #include "scap.h" #include "gen_filter.h" #include "../../driver/ppm_events_public.h" #include "settings.h" typedef class sinsp sinsp; typedef class sinsp_threadinfo sinsp_threadinfo; namespace test_helpers { class event_builder; class sinsp_mock; } /////////////////////////////////////////////////////////////////////////////// // Event arguments /////////////////////////////////////////////////////////////////////////////// typedef enum filtercheck_field_flags { EPF_NONE = 0, EPF_FILTER_ONLY = 1 << 0, ///< this field can only be used as a filter. EPF_PRINT_ONLY = 1 << 1, ///< this field can only be printed. EPF_REQUIRES_ARGUMENT = 1 << 2, ///< this field includes an argument, under the form 'property.argument'. EPF_TABLE_ONLY = 1 << 3, ///< this field is designed to be used in a table and won't appear in the list created by sysdig's '-l'. }filtercheck_field_flags; /*! \brief Information about a filter/formatting field. */ typedef struct filtercheck_field_info { ppm_param_type m_type; ///< Field type. filtercheck_field_flags m_flags; ///< Field flags. ppm_print_format m_print_format; ///< If this is a numeric field, this flag specifies if it should be rendered as octal, decimal or hex. char m_name[64]; ///< Field name. char m_description[1024]; ///< Field description. }filtercheck_field_info; /** @defgroup event Event manipulation * Classes to manipulate events, extract their content and convert them into strings. * @{ */ /*! \brief Wrapper that exports the libscap event tables. */ class SINSP_PUBLIC sinsp_evttables { public: const struct ppm_event_info* m_event_info; ///< List of events supported by the capture and analysis subsystems. Each entry fully documents an event and its parameters. const struct ppm_syscall_desc* m_syscall_info_table; ///< List of system calls that the capture subsystem recognizes, including the ones that are not decoded yet. }; /*! \brief Event parameter wrapper. This class describes a raw event coming from the driver. */ class SINSP_PUBLIC sinsp_evt_param { public: char* m_val; ///< Pointer to the event parameter data. uint16_t m_len; ///< Length os the parameter pointed by m_val. private: inline void init(char* valptr, uint16_t len) { m_val = valptr; m_len = len; } friend class sinsp_evt; }; /*! \brief Event class. This class is returned by \ref sinsp::next() and encapsulates the state related to a captured event, and includes a bunch of members to manipulate events and their parameters, including parsing, formatting and extracting state like the event process or FD. */ class SINSP_PUBLIC sinsp_evt : public gen_event { public: /*! \brief How to render an event parameter to string. */ enum param_fmt { PF_NORMAL = (1 << 0), ///< Normal screen output PF_JSON = (1 << 1), ///< Json formatting with data in normal screen format PF_SIMPLE = (1 << 2), ///< Reduced output, e.g. not type character for FDs PF_HEX = (1 << 3), ///< Hexadecimal output PF_HEXASCII = (1 << 4), ///< Hexadecimal + ASCII output PF_EOLS = (1 << 5), ///< Normal + end of lines PF_EOLS_COMPACT = (1 << 6), ///< Normal + end of lines but with no force EOL at the beginning PF_BASE64 = (1 << 7), ///< Base64 output PF_JSONEOLS = (1 << 8), ///< Json formatting with data in hexadecimal format PF_JSONHEX = (1 << 9), ///< Json formatting with data in hexadecimal format PF_JSONHEXASCII = (1 << 10), ///< Json formatting with data in hexadecimal + ASCII format PF_JSONBASE64 = (1 << 11), ///< Json formatting with data in base64 format }; /*! \brief Event subcategory specialization based on the fd type. */ enum subcategory { SC_UNKNOWN = 0, SC_NONE = 1, SC_OTHER = 2, SC_FILE = 3, SC_NET = 4, SC_IPC = 5, }; enum fd_number_type { INVALID_FD_NUM = -100000 }; /*! \brief Information regarding an event category, enriched with fd state. */ struct category { ppm_event_category m_category; ///< Event category from the driver subcategory m_subcategory; ///< Domain for IO and wait events }; sinsp_evt(); sinsp_evt(sinsp* inspector); ~sinsp_evt(); /*! \brief Set the inspector. */ void inspector(sinsp *value) { m_inspector = value; } /*! \brief Get the incremental number of this event. */ inline uint64_t get_num() { return m_evtnum; } /*! \brief Get the number of the CPU where this event was captured. */ inline int16_t get_cpuid() { return m_cpuid; } /*! \brief Get the event type. \note For a list of event types, refer to \ref etypes. */ inline uint16_t get_type() { return m_pevt->type; } /*! \brief Get the event's flags. */ inline ppm_event_flags get_info_flags() { return m_info->flags; } /*! \brief Get the event's category. */ inline ppm_event_category get_info_category() { return m_info->category; } /*! \brief Return the event direction: in or out. */ event_direction get_direction(); /*! \brief Get the event timestamp. \return The event timestamp, in nanoseconds from epoch */ inline uint64_t get_ts() { return m_pevt->ts; } /*! \brief Return the event name string, e.g. 'open' or 'socket'. */ const char* get_name(); /*! \brief Return the event category. */ inline ppm_event_category get_category() { return m_info->category; } /*! \brief Get the ID of the thread that generated the event. */ int64_t get_tid(); /*! \brief Return the information about the thread that generated the event. \param query_os_if_not_found if this is a live a capture and this flag is set to true, scan the /proc file system to find process information in case the thread is not in the table. */ sinsp_threadinfo* get_thread_info(bool query_os_if_not_found = false); /*! \brief Return the information about the FD on which this event operated. \note For events that are not I/O related, get_fd_info() returns NULL. */ inline sinsp_fdinfo_t* get_fd_info() { return m_fdinfo; } inline bool fdinfo_name_changed() { return m_fdinfo_name_changed; } inline void set_fdinfo_name_changed(bool changed) { m_fdinfo_name_changed = changed; } /*! \brief Return the number of the FD associated with this event. \note For events that are not I/O related, get_fd_num() returns sinsp_evt::INVALID_FD_NUM. */ int64_t get_fd_num(); /*! \brief Return the number of parameters that this event has. */ uint32_t get_num_params(); /*! \brief Get the name of one of the event parameters, e.g. 'fd' or 'addr'. \param id The parameter number. */ const char* get_param_name(uint32_t id); /*! \brief Get the metadata that describes one of this event's parameters. \param id The parameter number. \note Refer to the g_event_info structure in driver/event_table.c for a list of event descriptions. */ const struct ppm_param_info* get_param_info(uint32_t id); /*! \brief Get a parameter in raw format. \param id The parameter number. */ sinsp_evt_param* get_param(uint32_t id); /*! \brief Get a parameter in raw format. \param name The parameter name. */ const sinsp_evt_param* get_param_value_raw(const char* name); /*! \brief Get a parameter as a C++ string. \param name The parameter name. \param resolved If true, the library will try to resolve the parameter before returning it. For example, and FD number will be converted into the correspondent file, TCP tuple, etc. */ std::string get_param_value_str(const std::string& name, bool resolved = true); /*! \brief Return the event's category, based on the event type and the FD on which the event operates. */ void get_category(OUT sinsp_evt::category* cat); #ifdef HAS_FILTERING /*! \brief Return true if the event has been rejected by the filtering system. */ bool is_filtered_out(); scap_dump_flags get_dump_flags(OUT bool* should_drop); #endif /*! \brief Return whether or not falco should consider this event. (Generally, these events are automatically filtered out, but some events related to internal tracking are returned by next() anyway). */ bool falco_consider(); inline uint16_t get_source() { return ESRC_SINSP; } // Doxygen doesn't understand VISIBILITY_PRIVATE #ifdef _DOXYGEN private: #endif void set_iosize(uint32_t size); uint32_t get_iosize(); const char* get_param_as_str(uint32_t id, OUT const char** resolved_str, param_fmt fmt = PF_NORMAL); Json::Value get_param_as_json(uint32_t id, OUT const char** resolved_str, param_fmt fmt = PF_NORMAL); const char* get_param_value_str(const char* name, OUT const char** resolved_str, param_fmt fmt = PF_NORMAL); inline void init() { m_flags = EF_NONE; m_info = &(m_event_info_table[m_pevt->type]); m_tinfo_ref.reset(); m_tinfo = NULL; m_fdinfo = NULL; m_fdinfo_name_changed = false; m_iosize = 0; m_poriginal_evt = NULL; } inline void init(uint8_t* evdata, uint16_t cpuid) { m_flags = EF_NONE; m_pevt = (scap_evt *)evdata; m_info = &(m_event_info_table[m_pevt->type]); m_tinfo_ref.reset(); m_tinfo = NULL; m_fdinfo = NULL; m_fdinfo_name_changed = false; m_iosize = 0; m_cpuid = cpuid; m_evtnum = 0; m_poriginal_evt = NULL; } inline void init(scap_evt *scap_event, ppm_event_info * ppm_event, sinsp_threadinfo *threadinfo, sinsp_fdinfo_t *fdinfo) { m_pevt = scap_event; m_info = ppm_event; m_tinfo_ref.reset(); // we don't own the threadinfo so don't try to manage its lifetime m_tinfo = threadinfo; m_fdinfo = fdinfo; } inline void load_params() { uint32_t j; uint32_t nparams; sinsp_evt_param par; // If we're reading a capture created with a newer version, it may contain // new parameters. If instead we're reading an older version, the current // event table entry may contain new parameters. // Use the minimum between the two values. nparams = m_info->nparams < m_pevt->nparams ? m_info->nparams : m_pevt->nparams; uint16_t *lens = (uint16_t *)((char *)m_pevt + sizeof(struct ppm_evt_hdr)); // The offset in the block is instead always based on the capture value. char *valptr = (char *)lens + m_pevt->nparams * sizeof(uint16_t); m_params.clear(); for(j = 0; j < nparams; j++) { par.init(valptr, lens[j]); m_params.push_back(par); valptr += lens[j]; } } std::string get_param_value_str(uint32_t id, bool resolved); std::string get_param_value_str(const char* name, bool resolved = true); char* render_fd(int64_t fd, const char** resolved_str, sinsp_evt::param_fmt fmt); int render_fd_json(Json::Value *ret, int64_t fd, const char** resolved_str, sinsp_evt::param_fmt fmt); uint32_t get_dump_flags(); VISIBILITY_PRIVATE enum flags { SINSP_EF_NONE = 0, SINSP_EF_PARAMS_LOADED = 1, SINSP_EF_IS_TRACER = (1 << 1), }; sinsp* m_inspector; scap_evt* m_pevt; scap_evt* m_poriginal_evt; // This is used when the original event is replaced by a different one (e.g. in the case of user events) char *m_pevt_storage; // In some cases an alternate buffer is used to hold m_pevt. This points to that storage. uint16_t m_cpuid; uint64_t m_evtnum; uint32_t m_flags; bool m_params_loaded; const struct ppm_event_info* m_info; std::vector m_params; std::vector m_paramstr_storage; std::vector m_resolved_paramstr_storage; // reference to keep threadinfo alive. currently only used for synthetic container event thread info // it should either be null, or point to the same place as m_tinfo std::shared_ptr m_tinfo_ref; sinsp_threadinfo* m_tinfo; sinsp_fdinfo_t* m_fdinfo; // If true, then the associated fdinfo changed names as a part // of parsing this event. bool m_fdinfo_name_changed; uint32_t m_iosize; int32_t m_errorcode; int32_t m_rawbuf_str_len; #ifdef HAS_FILTERING bool m_filtered_out; #endif const struct ppm_event_info* m_event_info_table; friend class sinsp; friend class sinsp_parser; friend class sinsp_threadinfo; friend class sinsp_analyzer; friend class sinsp_filter_check_event; friend class sinsp_filter_check_thread; friend class sinsp_evttype_filter; friend class sinsp_dumper; friend class sinsp_analyzer_fd_listener; friend class sinsp_analyzer_parsers; friend class lua_cbacks; friend class sinsp_proto_detector; friend class sinsp_container_manager; friend class sinsp_table; friend class sinsp_cursesui; friend class sinsp_baseliner; friend class capture_job_handler; friend class capture_job; friend class sinsp_memory_dumper; friend class sinsp_memory_dumper_job; friend class test_helpers::event_builder; friend class test_helpers::sinsp_mock; }; /*@}*/ sysdig-0.26.4/userspace/libsinsp/eventformatter.cpp000066400000000000000000000172271352731327100225100ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "sinsp.h" #include "sinsp_int.h" #include "filter.h" #include "filterchecks.h" #include "eventformatter.h" /////////////////////////////////////////////////////////////////////////////// // rawstring_check implementation /////////////////////////////////////////////////////////////////////////////// #ifdef HAS_FILTERING extern sinsp_filter_check_list g_filterlist; sinsp_evt_formatter::sinsp_evt_formatter(sinsp* inspector, const string& fmt) { m_inspector = inspector; set_format(fmt); } sinsp_evt_formatter::~sinsp_evt_formatter() { uint32_t j; for(j = 0; j < m_chks_to_free.size(); j++) { delete m_chks_to_free[j]; } } void sinsp_evt_formatter::set_format(const string& fmt) { uint32_t j; uint32_t last_nontoken_str_start = 0; string lfmt(fmt); if(lfmt == "") { throw sinsp_exception("empty formatting token"); } // // If the string starts with a *, it means that we are ok with printing // the string even when not all the values it specifies are set. // if(lfmt[0] == '*') { m_require_all_values = false; lfmt.erase(0, 1); } else { m_require_all_values = true; } // // Parse the string and extract the tokens // const char* cfmt = lfmt.c_str(); m_tokens.clear(); uint32_t lfmtlen = (uint32_t)lfmt.length(); for(j = 0; j < lfmtlen; j++) { if(cfmt[j] == '%') { int toklen = 0; if(last_nontoken_str_start != j) { rawstring_check* newtkn = new rawstring_check(lfmt.substr(last_nontoken_str_start, j - last_nontoken_str_start)); m_tokens.emplace_back(make_pair("", newtkn)); m_tokenlens.push_back(0); m_chks_to_free.push_back(newtkn); } if(j == lfmtlen - 1) { throw sinsp_exception("invalid formatting syntax: formatting cannot end with a %"); } // // If the field specifier starts with a number, it means that we have a length modifier // if(isdigit(cfmt[j + 1])) { // // Parse the token length // sscanf(cfmt+ j + 1, "%d", &toklen); // // Advance until the beginning of the field name // while(true) { if(j == lfmtlen - 1) { throw sinsp_exception("invalid formatting syntax: formatting cannot end with a number"); } else if(isdigit(cfmt[j + 1])) { j++; continue; } else { break; } } } sinsp_filter_check* chk = g_filterlist.new_filter_check_from_fldname(string(cfmt + j + 1), m_inspector, false); if(chk == NULL) { throw sinsp_exception("invalid formatting token " + string(cfmt + j + 1)); } m_chks_to_free.push_back(chk); const char * fstart = cfmt + j + 1; uint32_t fsize = chk->parse_field_name(fstart, true, false); j += fsize; ASSERT(j <= lfmt.length()); m_tokens.emplace_back(make_pair(string(fstart, fsize), chk)); m_tokenlens.push_back(toklen); last_nontoken_str_start = j + 1; } } if(last_nontoken_str_start != j) { sinsp_filter_check * chk = new rawstring_check(lfmt.substr(last_nontoken_str_start, j - last_nontoken_str_start)); m_tokens.emplace_back(make_pair("", chk)); m_chks_to_free.push_back(chk); m_tokenlens.push_back(0); } } bool sinsp_evt_formatter::on_capture_end(OUT string* res) { res->clear(); return res->size() > 0; } bool sinsp_evt_formatter::resolve_tokens(sinsp_evt *evt, map& values) { bool retval = true; const filtercheck_field_info* fi; uint32_t j = 0; ASSERT(m_tokenlens.size() == m_tokens.size()); for(j = 0; j < m_tokens.size(); j++) { char* str = m_tokens[j].second->tostring(evt); if(str == NULL) { if(m_require_all_values) { retval = false; break; } else { str = (char*)""; } } fi = m_tokens[j].second->get_field_info(); if(fi) { values[m_tokens[j].first] = string(str); } } return retval; } bool sinsp_evt_formatter::tostring(sinsp_evt* evt, OUT string* res) { bool retval = true; const filtercheck_field_info* fi; uint32_t j = 0; vector::iterator it; res->clear(); ASSERT(m_tokenlens.size() == m_tokens.size()); for(j = 0; j < m_tokens.size(); j++) { if(m_inspector->get_buffer_format() == sinsp_evt::PF_JSON || m_inspector->get_buffer_format() == sinsp_evt::PF_JSONEOLS || m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEX || m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEXASCII || m_inspector->get_buffer_format() == sinsp_evt::PF_JSONBASE64) { Json::Value json_value = m_tokens[j].second->tojson(evt); if(retval == false) { continue; } if(json_value == Json::nullValue && m_require_all_values) { retval = false; continue; } fi = m_tokens[j].second->get_field_info(); if(fi) { m_root[m_tokens[j].first] = m_tokens[j].second->tojson(evt); } } else { char* str = m_tokens[j].second->tostring(evt); if(retval == false) { continue; } if(str == NULL) { if(m_require_all_values) { retval = false; continue; } else { str = (char*)""; } } uint32_t tks = m_tokenlens[j]; if(tks != 0) { string sstr(str); sstr.resize(tks, ' '); (*res) += sstr; } else { (*res) += str; } } } if(m_inspector->get_buffer_format() == sinsp_evt::PF_JSON || m_inspector->get_buffer_format() == sinsp_evt::PF_JSONEOLS || m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEX || m_inspector->get_buffer_format() == sinsp_evt::PF_JSONHEXASCII || m_inspector->get_buffer_format() == sinsp_evt::PF_JSONBASE64) { (*res) = m_writer.write(m_root); (*res) = res->substr(0, res->size() - 1); } return retval; } #else // HAS_FILTERING sinsp_evt_formatter::sinsp_evt_formatter(sinsp* inspector, const string& fmt) { } void sinsp_evt_formatter::set_format(const string& fmt) { throw sinsp_exception("sinsp_evt_formatter unvavailable because it was not compiled in the library"); } bool sinsp_evt_formatter::resolve_tokens(sinsp_evt *evt, map& values) { throw sinsp_exception("sinsp_evt_formatter unvavailable because it was not compiled in the library"); } bool sinsp_evt_formatter::tostring(sinsp_evt* evt, OUT string* res) { throw sinsp_exception("sinsp_evt_formatter unvavailable because it was not compiled in the library"); } #endif // HAS_FILTERING sinsp_evt_formatter_cache::sinsp_evt_formatter_cache(sinsp *inspector) : m_inspector(inspector) { } sinsp_evt_formatter_cache::~sinsp_evt_formatter_cache() { } std::shared_ptr& sinsp_evt_formatter_cache::get_cached_formatter(string &format) { auto it = m_formatter_cache.lower_bound(format); if(it == m_formatter_cache.end() || it->first != format) { it = m_formatter_cache.emplace_hint(it, std::make_pair(format, make_shared(m_inspector, format))); } return it->second; } bool sinsp_evt_formatter_cache::resolve_tokens(sinsp_evt *evt, string &format, map& values) { return get_cached_formatter(format)->resolve_tokens(evt, values); } bool sinsp_evt_formatter_cache::tostring(sinsp_evt *evt, string &format, OUT string *res) { return get_cached_formatter(format)->tostring(evt, res); } sysdig-0.26.4/userspace/libsinsp/eventformatter.h000066400000000000000000000074131352731327100221510ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include class sinsp_filter_check; /** @defgroup event Event manipulation * @{ */ /*! \brief Event to string converter class. This class can be used to format an event into a string, based on an arbitrary format. */ class SINSP_PUBLIC sinsp_evt_formatter { public: /*! \brief Constructs a formatter. \param inspector Pointer to the inspector instance that will generate the events to be formatter. \param fmt The printf-like format to use. The accepted format is the same as the one of the sysdig '-p' command line flag, so refer to the sysdig manual for details. */ sinsp_evt_formatter(sinsp* inspector, const string& fmt); ~sinsp_evt_formatter(); /*! \brief Resolve all the formatted tokens and return them in a key/value map. \param evt Pointer to the event to be converted into string. \param res Reference to the map that will be filled with the result. \return true if all the tokens can be retrieved successfully, false otherwise. */ bool resolve_tokens(sinsp_evt *evt, map& values); /*! \brief Fills res with the string rendering of the event. \param evt Pointer to the event to be converted into string. \param res Pointer to the string that will be filled with the result. \return true if the string should be shown (based on the initial *), false otherwise. */ bool tostring(sinsp_evt* evt, OUT string* res); /*! \brief Fills res with end of capture string rendering of the event. \param res Pointer to the string that will be filled with the result. \return true if there is a string to show (based on the format), false otherwise. */ bool on_capture_end(OUT string* res); private: void set_format(const string& fmt); // vector of (full string of the token, filtercheck) pairs // e.g. ("proc.aname[2], ptr to sinsp_filter_check_thread) vector> m_tokens; vector m_tokenlens; sinsp* m_inspector; bool m_require_all_values; vector m_chks_to_free; Json::Value m_root; Json::FastWriter m_writer; }; /*! \brief Caching version of sinsp_evt_formatter This class is a wrapper around sinsp_evt_formatter, maintaining a cache of previously seen formatters. It avoids the overhead of recreating sinsp_evt_formatter objects for each event. */ class SINSP_PUBLIC sinsp_evt_formatter_cache { public: sinsp_evt_formatter_cache(sinsp *inspector); virtual ~sinsp_evt_formatter_cache(); // Resolve the tokens inside format and return them as a key/value map. // Creates a new sinsp_evt_formatter object if necessary. bool resolve_tokens(sinsp_evt *evt, std::string &format, map& values); // Fills in res with the event formatted according to // format. Creates a new sinsp_evt_formatter object if // necessary. bool tostring(sinsp_evt *evt, std::string &format, OUT std::string *res); private: // Get the formatter for this format string. Creates a new // sinsp_evt_formatter object if necessary. std::shared_ptr& get_cached_formatter(string &format); std::map> m_formatter_cache; sinsp *m_inspector; }; /*@}*/ sysdig-0.26.4/userspace/libsinsp/fdinfo.cpp000066400000000000000000000224421352731327100207030ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #ifndef _WIN32 #define __STDC_FORMAT_MACROS #include #include #endif #include "sinsp.h" #include "sinsp_int.h" /////////////////////////////////////////////////////////////////////////////// // sinsp_fdinfo inomlementation /////////////////////////////////////////////////////////////////////////////// template<> sinsp_fdinfo_t::sinsp_fdinfo() { m_type = SCAP_FD_UNINITIALIZED; m_flags = FLAGS_NONE; m_callbaks = NULL; m_usrstate = NULL; } template<> void sinsp_fdinfo_t::reset() { m_type = SCAP_FD_UNINITIALIZED; m_flags = FLAGS_NONE; delete(m_callbaks); m_callbaks = NULL; m_usrstate = NULL; } template<> string* sinsp_fdinfo_t::tostring() { return &m_name; } template<> char sinsp_fdinfo_t::get_typechar() { switch(m_type) { case SCAP_FD_FILE_V2: case SCAP_FD_FILE: return CHAR_FD_FILE; case SCAP_FD_IPV4_SOCK: return CHAR_FD_IPV4_SOCK; case SCAP_FD_IPV6_SOCK: return CHAR_FD_IPV6_SOCK; case SCAP_FD_DIRECTORY: return CHAR_FD_DIRECTORY; case SCAP_FD_IPV4_SERVSOCK: return CHAR_FD_IPV4_SERVSOCK; case SCAP_FD_IPV6_SERVSOCK: return CHAR_FD_IPV6_SERVSOCK; case SCAP_FD_FIFO: return CHAR_FD_FIFO; case SCAP_FD_UNIX_SOCK: return CHAR_FD_UNIX_SOCK; case SCAP_FD_EVENT: return CHAR_FD_EVENT; case SCAP_FD_UNKNOWN: return CHAR_FD_UNKNOWN; case SCAP_FD_UNSUPPORTED: return CHAR_FD_UNSUPPORTED; case SCAP_FD_SIGNALFD: return CHAR_FD_SIGNAL; case SCAP_FD_EVENTPOLL: return CHAR_FD_EVENTPOLL; case SCAP_FD_INOTIFY: return CHAR_FD_INOTIFY; case SCAP_FD_TIMERFD: return CHAR_FD_TIMERFD; case SCAP_FD_NETLINK: return CHAR_FD_NETLINK; default: // ASSERT(false); return '?'; } } template<> char* sinsp_fdinfo_t::get_typestring() { switch(m_type) { case SCAP_FD_FILE_V2: case SCAP_FD_FILE: return (char*)"file"; case SCAP_FD_DIRECTORY: return (char*)"directory"; case SCAP_FD_IPV4_SOCK: case SCAP_FD_IPV4_SERVSOCK: return (char*)"ipv4"; case SCAP_FD_IPV6_SOCK: case SCAP_FD_IPV6_SERVSOCK: return (char*)"ipv6"; case SCAP_FD_UNIX_SOCK: return (char*)"unix"; case SCAP_FD_FIFO: return (char*)"pipe"; case SCAP_FD_EVENT: return (char*)"event"; case SCAP_FD_SIGNALFD: return (char*)"signalfd"; case SCAP_FD_EVENTPOLL: return (char*)"eventpoll"; case SCAP_FD_INOTIFY: return (char*)"inotify"; case SCAP_FD_TIMERFD: return (char*)"timerfd"; case SCAP_FD_NETLINK: return (char*)"netlink"; default: return (char*)""; } } template<> string sinsp_fdinfo_t::tostring_clean() { string m_tstr = m_name; sanitize_string(m_tstr); return m_tstr; } template<> void sinsp_fdinfo_t::add_filename(const char* fullpath) { m_name = fullpath; } template<> bool sinsp_fdinfo_t::set_net_role_by_guessing(sinsp* inspector, sinsp_threadinfo* ptinfo, sinsp_fdinfo_t* pfdinfo, bool incoming) { // // If this process owns the port, mark it as server, otherwise mark it as client // if(ptinfo->is_bound_to_port(pfdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport)) { if(ptinfo->uses_client_port(pfdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport)) { goto wildass_guess; } pfdinfo->set_role_server(); return true; } else { pfdinfo->set_role_client(); return true; } wildass_guess: if(!(pfdinfo->m_flags & (sinsp_fdinfo_t::FLAGS_ROLE_CLIENT | sinsp_fdinfo_t::FLAGS_ROLE_SERVER))) { // // We just assume that a server usually starts with a read and a client with a write // if(incoming) { pfdinfo->set_role_server(); } else { pfdinfo->set_role_client(); } } return true; } template<> scap_l4_proto sinsp_fdinfo_t::get_l4proto() { scap_fd_type evt_type = m_type; if(evt_type == SCAP_FD_IPV4_SOCK) { if((scap_l4_proto)m_sockinfo.m_ipv4info.m_fields.m_l4proto == SCAP_L4_RAW) { return SCAP_L4_RAW; } if(is_role_none()) { return SCAP_L4_NA; } return (scap_l4_proto)(m_sockinfo.m_ipv4info.m_fields.m_l4proto); } else if(evt_type == SCAP_FD_IPV4_SERVSOCK) { return (scap_l4_proto)(m_sockinfo.m_ipv4serverinfo.m_l4proto); } else if(evt_type == SCAP_FD_IPV6_SOCK) { if((scap_l4_proto)m_sockinfo.m_ipv6info.m_fields.m_l4proto == SCAP_L4_RAW) { return SCAP_L4_RAW; } if(is_role_none()) { return SCAP_L4_NA; } return (scap_l4_proto)(m_sockinfo.m_ipv6info.m_fields.m_l4proto); } else if(evt_type == SCAP_FD_IPV6_SERVSOCK) { return (scap_l4_proto)(m_sockinfo.m_ipv6serverinfo.m_l4proto); } else { return SCAP_L4_NA; } } template<> void sinsp_fdinfo_t::register_event_callback(sinsp_pd_callback_type etype, sinsp_protodecoder* dec) { if(this->m_callbaks == NULL) { m_callbaks = new fd_callbacks_info(); } switch(etype) { case CT_READ: m_callbaks->m_read_callbacks.push_back(dec); break; case CT_WRITE: m_callbaks->m_write_callbacks.push_back(dec); break; default: ASSERT(false); break; } return; } template<> void sinsp_fdinfo_t::unregister_event_callback(sinsp_pd_callback_type etype, sinsp_protodecoder* dec) { vector::iterator it; if(m_callbaks == NULL) { ASSERT(false); return; } switch(etype) { case CT_READ: for(it = m_callbaks->m_read_callbacks.begin(); it != m_callbaks->m_read_callbacks.end(); ++it) { if(*it == dec) { m_callbaks->m_read_callbacks.erase(it); return; } } break; case CT_WRITE: for(it = m_callbaks->m_write_callbacks.begin(); it != m_callbaks->m_write_callbacks.end(); ++it) { if(*it == dec) { m_callbaks->m_write_callbacks.erase(it); return; } } break; default: ASSERT(false); break; } return; } /////////////////////////////////////////////////////////////////////////////// // sinsp_fdtable implementation /////////////////////////////////////////////////////////////////////////////// sinsp_fdtable::sinsp_fdtable(sinsp* inspector) { m_inspector = inspector; reset_cache(); } sinsp_fdinfo_t* sinsp_fdtable::add(int64_t fd, sinsp_fdinfo_t* fdinfo) { // // Look for the FD in the table // auto it = m_table.find(fd); // Three possible exits here: // 1. fd is not on the table // a. the table size is under the limit so create a new entry // b. table size is over the limit, discard the fd // 2. fd is already in the table, replace it if(it == m_table.end()) { if(m_table.size() < m_inspector->m_max_fdtable_size) { // // No entry in the table, this is the normal case // m_last_accessed_fd = -1; #ifdef GATHER_INTERNAL_STATS m_inspector->m_stats.m_n_added_fds++; #endif pair::iterator, bool> insert_res = m_table.emplace(fd, *fdinfo); return &(insert_res.first->second); } else { return nullptr; } } else { // // the fd is already in the table. // if(it->second.m_flags & sinsp_fdinfo_t::FLAGS_CLOSE_IN_PROGRESS) { // // Sometimes an FD-creating syscall can be called on an FD that is being closed (i.e // the close enter has arrived but the close exit has not arrived yet). // If this is the case, mark the new entry so that the successive close exit won't // destroy it. // fdinfo->m_flags &= ~sinsp_fdinfo_t::FLAGS_CLOSE_IN_PROGRESS; fdinfo->m_flags |= sinsp_fdinfo_t::FLAGS_CLOSE_CANCELED; m_table[CANCELED_FD_NUMBER] = it->second; } else { // // This can happen if: // - the event is a dup2 or dup3 that overwrites an existing FD (perfectly legal) // - a close() has been dropped when capturing // - an fd has been closed by clone() or execve() (it happens when the fd is opened with the FD_CLOEXEC flag, // which we don't currently parse. // In either case, removing the old fd, replacing it with the new one and keeping going is a reasonable // choice. We include an assertion to catch the situation. // // XXX Can't have this enabled until the FD_CLOEXEC flag is supported //ASSERT(false); } // // Replace the fd as a struct copy // it->second.copy(*fdinfo, true); return &(it->second); } } void sinsp_fdtable::erase(int64_t fd) { unordered_map::iterator fdit = m_table.find(fd); if(fd == m_last_accessed_fd) { m_last_accessed_fd = -1; } if(fdit == m_table.end()) { // // Looks like there's no fd to remove. // Either the fd creation event was dropped or (more likely) our logic doesn't support the // call that created this fd. The assertion will detect it, while in release mode we just // keep going. // ASSERT(false); #ifdef GATHER_INTERNAL_STATS m_inspector->m_stats.m_n_failed_fd_lookups++; #endif } else { m_table.erase(fdit); #ifdef GATHER_INTERNAL_STATS m_inspector->m_stats.m_n_noncached_fd_lookups++; m_inspector->m_stats.m_n_removed_fds++; #endif } } void sinsp_fdtable::clear() { m_table.clear(); } size_t sinsp_fdtable::size() { return m_table.size(); } void sinsp_fdtable::reset_cache() { m_last_accessed_fd = -1; } sysdig-0.26.4/userspace/libsinsp/fdinfo.h000066400000000000000000000302421352731327100203450ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include "sinsp_pd_callback_type.h" #include #include #ifdef _WIN32 #define CANCELED_FD_NUMBER INT64_MAX #else #define CANCELED_FD_NUMBER std::numeric_limits::max() #endif class sinsp_protodecoder; // fd type characters #define CHAR_FD_FILE 'f' #define CHAR_FD_IPV4_SOCK '4' #define CHAR_FD_IPV6_SOCK '6' #define CHAR_FD_DIRECTORY 'd' #define CHAR_FD_IPV4_SERVSOCK '4' #define CHAR_FD_IPV6_SERVSOCK '6' #define CHAR_FD_FIFO 'p' #define CHAR_FD_UNIX_SOCK 'u' #define CHAR_FD_EVENT 'e' #define CHAR_FD_UNKNOWN 'o' #define CHAR_FD_UNSUPPORTED 'X' #define CHAR_FD_SIGNAL 's' #define CHAR_FD_EVENTPOLL 'l' #define CHAR_FD_INOTIFY 'i' #define CHAR_FD_TIMERFD 't' #define CHAR_FD_NETLINK 'n' /** @defgroup state State management * A collection of classes to query process and FD state. * @{ */ typedef union _sinsp_sockinfo { ipv4tuple m_ipv4info; ///< The tuple if this an IPv4 socket. ipv6tuple m_ipv6info; ///< The tuple if this an IPv6 socket. ipv4serverinfo m_ipv4serverinfo; ///< Information about an IPv4 server socket. ipv6serverinfo m_ipv6serverinfo; ///< Information about an IPv6 server socket. unix_tuple m_unixinfo; ///< The tuple if this a unix socket. }sinsp_sockinfo; class fd_callbacks_info { public: std::vector m_write_callbacks; std::vector m_read_callbacks; }; /*! \brief File Descriptor information class. This class contains the full state for a FD, and a bunch of functions to manipulate FDs and retrieve FD information. \note As a library user, you won't need to construct thread objects. Rather, you get them by calling \ref sinsp_evt::get_fd_info or \ref sinsp_threadinfo::get_fd. */template class SINSP_PUBLIC sinsp_fdinfo { public: sinsp_fdinfo(); sinsp_fdinfo (const sinsp_fdinfo &other) { copy(other, false); } ~sinsp_fdinfo() { if(m_callbaks != NULL) { delete m_callbaks; } if(m_usrstate != NULL) { delete m_usrstate; } } sinsp_fdinfo& operator=(const sinsp_fdinfo& other) { copy(other, true); return *this; } void reset(); std::string* tostring(); inline void copy(const sinsp_fdinfo &other, bool free_state) { m_type = other.m_type; m_openflags = other.m_openflags; m_sockinfo = other.m_sockinfo; m_name = other.m_name; m_oldname = other.m_oldname; m_flags = other.m_flags; m_dev = other.m_dev; m_ino = other.m_ino; if(free_state) { if(m_callbaks != NULL) { delete m_callbaks; } if(m_usrstate != NULL) { delete m_usrstate; } } if(other.m_callbaks != NULL) { m_callbaks = new fd_callbacks_info(); *m_callbaks = *other.m_callbaks; } else { m_callbaks = NULL; } if(other.m_usrstate != NULL) { m_usrstate = new T(*other.m_usrstate); } else { m_usrstate = NULL; } } /*! \brief Return a single ASCII character that identifies the FD type. Refer to the CHAR_FD_* defines in this fdinfo.h. */ char get_typechar(); /*! \brief Return an ASCII string that identifies the FD type. Can be on of 'file', 'directory', ipv4', 'ipv6', 'unix', 'pipe', 'event', 'signalfd', 'eventpoll', 'inotify', 'signalfd'. */ char* get_typestring(); /*! \brief Return the fd name, after removing unprintable or invalid characters from it. */ std::string tostring_clean(); /*! \brief Returns true if this is a unix socket. */ bool is_unix_socket() { return m_type == SCAP_FD_UNIX_SOCK; } /*! \brief Returns true if this is an IPv4 socket. */ bool is_ipv4_socket() { return m_type == SCAP_FD_IPV4_SOCK; } /*! \brief Returns true if this is an IPv4 socket. */ bool is_ipv6_socket() { return m_type == SCAP_FD_IPV6_SOCK; } /*! \brief Returns true if this is a UDP socket. */ bool is_udp_socket() { return m_type == SCAP_FD_IPV4_SOCK && m_sockinfo.m_ipv4info.m_fields.m_l4proto == SCAP_L4_UDP; } /*! \brief Returns true if this is a unix TCP. */ bool is_tcp_socket() { return m_type == SCAP_FD_IPV4_SOCK && m_sockinfo.m_ipv4info.m_fields.m_l4proto == SCAP_L4_TCP; } /*! \brief Returns true if this is a pipe. */ bool is_pipe() { return m_type == SCAP_FD_FIFO; } /*! \brief Returns true if this is a file. */ bool is_file() { return m_type == SCAP_FD_FILE || m_type == SCAP_FD_FILE_V2; } /*! \brief Returns true if this is a directory. */ bool is_directory() { return m_type == SCAP_FD_DIRECTORY; } uint16_t get_serverport() { if(m_type == SCAP_FD_IPV4_SOCK) { return m_sockinfo.m_ipv4info.m_fields.m_dport; } else if(m_type == SCAP_FD_IPV6_SOCK) { return m_sockinfo.m_ipv6info.m_fields.m_dport; } else { return 0; } } uint32_t get_device() const { return m_dev; } // see new_encode_dev in include/linux/kdev_t.h uint32_t get_device_major() const { return (m_dev & 0xfff00) >> 8; } // see new_encode_dev in include/linux/kdev_t.h uint32_t get_device_minor() const { return (m_dev & 0xff) | ((m_dev >> 12) & 0xfff00); } /*! \brief If this is a socket, returns the IP protocol. Otherwise, return SCAP_FD_UNKNOWN. */ scap_l4_proto get_l4proto(); /*! \brief Used by protocol decoders to register callbacks related to this FD. */ void register_event_callback(sinsp_pd_callback_type etype, sinsp_protodecoder* dec); /*! \brief Used by protocol decoders to unregister callbacks related to this FD. */ void unregister_event_callback(sinsp_pd_callback_type etype, sinsp_protodecoder* dec); /*! \brief Return true if this FD is a socket server */ inline bool is_role_server() { return (m_flags & FLAGS_ROLE_SERVER) == FLAGS_ROLE_SERVER; } /*! \brief Return true if this FD is a socket client */ inline bool is_role_client() { return (m_flags & FLAGS_ROLE_CLIENT) == FLAGS_ROLE_CLIENT; } /*! \brief Return true if this FD is neither a client nor a server */ inline bool is_role_none() { return (m_flags & (FLAGS_ROLE_CLIENT | FLAGS_ROLE_SERVER)) == 0; } inline bool is_socket_connected() { return (m_flags & FLAGS_SOCKET_CONNECTED) == FLAGS_SOCKET_CONNECTED; } inline bool is_socket_pending() { return (m_flags & FLAGS_CONNECTION_PENDING) == FLAGS_CONNECTION_PENDING; } inline bool is_socket_failed() { return (m_flags & FLAGS_CONNECTION_FAILED) == FLAGS_CONNECTION_FAILED; } inline bool is_cloned() { return (m_flags & FLAGS_IS_CLONED) == FLAGS_IS_CLONED; } scap_fd_type m_type; ///< The fd type, e.g. file, directory, IPv4 socket... uint32_t m_openflags; ///< If this FD is a file, the flags that were used when opening it. See the PPM_O_* definitions in driver/ppm_events_public.h. /*! \brief Socket-specific state. This is uninitialized for non-socket FDs. */ sinsp_sockinfo m_sockinfo; std::string m_name; ///< Human readable rendering of this FD. For files, this is the full file name. For sockets, this is the tuple. And so on. std::string m_oldname; // The name of this fd at the beginning of event parsing. Used to detect name changes that result from parsing an event. inline bool has_decoder_callbacks() { return (m_callbaks != NULL); } VISIBILITY_PRIVATE // Doxygen doesn't understand VISIBILITY_PRIVATE #ifdef _DOXYGEN private: #endif /*! \brief FD flags. */ enum flags { FLAGS_NONE = 0, FLAGS_FROM_PROC = (1 << 0), //FLAGS_TRANSACTION = (1 << 1), FLAGS_ROLE_CLIENT = (1 << 2), FLAGS_ROLE_SERVER = (1 << 3), FLAGS_CLOSE_IN_PROGRESS = (1 << 4), FLAGS_CLOSE_CANCELED = (1 << 5), FLAGS_IS_SOCKET_PIPE = (1 << 6), FLAGS_IS_TRACER_FILE = (1 << 7), FLAGS_IS_TRACER_FD = (1 << 8), FLAGS_IS_NOT_TRACER_FD = (1 << 9), FLAGS_IN_BASELINE_R = (1 << 10), FLAGS_IN_BASELINE_RW = (1 << 11), FLAGS_IN_BASELINE_OTHER = (1 << 12), FLAGS_SOCKET_CONNECTED = (1 << 13), FLAGS_IS_CLONED = (1 << 14), FLAGS_CONNECTION_PENDING = (1 << 15), FLAGS_CONNECTION_FAILED = (1 << 16), }; void add_filename(const char* fullpath); inline bool is_transaction() { return (m_usrstate != NULL); } inline void set_role_server() { m_flags |= FLAGS_ROLE_SERVER; } inline void set_role_client() { m_flags |= FLAGS_ROLE_CLIENT; } bool set_net_role_by_guessing(sinsp* inspector, sinsp_threadinfo* ptinfo, sinsp_fdinfo_t* pfdinfo, bool incoming); inline void reset_flags() { m_flags = FLAGS_NONE; } inline void set_socketpipe() { m_flags |= FLAGS_IS_SOCKET_PIPE; } inline bool is_socketpipe() { return (m_flags & FLAGS_IS_SOCKET_PIPE) == FLAGS_IS_SOCKET_PIPE; } inline bool has_no_role() { return !is_role_client() && !is_role_server(); } inline void set_inpipeline_r() { m_flags |= FLAGS_IN_BASELINE_R; } inline void set_inpipeline_rw() { m_flags |= FLAGS_IN_BASELINE_RW; } inline void set_inpipeline_other() { m_flags |= FLAGS_IN_BASELINE_OTHER; } inline void reset_inpipeline() { m_flags &= ~FLAGS_IN_BASELINE_R; m_flags &= ~FLAGS_IN_BASELINE_RW; m_flags &= ~FLAGS_IN_BASELINE_OTHER; } inline bool is_inpipeline_r() { return (m_flags & FLAGS_IN_BASELINE_R) == FLAGS_IN_BASELINE_R; } inline bool is_inpipeline_rw() { return (m_flags & FLAGS_IN_BASELINE_RW) == FLAGS_IN_BASELINE_RW; } inline bool is_inpipeline_other() { return (m_flags & FLAGS_IN_BASELINE_OTHER) == FLAGS_IN_BASELINE_OTHER; } inline void set_socket_connected() { m_flags &= ~(FLAGS_CONNECTION_PENDING | FLAGS_CONNECTION_FAILED); m_flags |= FLAGS_SOCKET_CONNECTED; } inline void set_socket_pending() { m_flags &= ~(FLAGS_SOCKET_CONNECTED | FLAGS_CONNECTION_FAILED); m_flags |= FLAGS_CONNECTION_PENDING; } inline void set_socket_failed() { m_flags &= ~(FLAGS_SOCKET_CONNECTED | FLAGS_CONNECTION_PENDING); m_flags |= FLAGS_CONNECTION_FAILED; } inline void set_is_cloned() { m_flags |= FLAGS_IS_CLONED; } T* m_usrstate; uint32_t m_flags; uint32_t m_dev; uint64_t m_ino; fd_callbacks_info* m_callbaks; friend class sinsp; friend class sinsp_parser; friend class sinsp_threadinfo; friend class sinsp_analyzer; friend class thread_analyzer_info; friend class sinsp_analyzer_fd_listener; friend class sinsp_fdtable; friend class sinsp_filter_check_fd; friend class sinsp_filter_check_event; friend class lua_cbacks; friend class sinsp_proto_detector; friend class sinsp_baseliner; }; /*@}*/ /////////////////////////////////////////////////////////////////////////////// // fd info table /////////////////////////////////////////////////////////////////////////////// class sinsp_fdtable { public: sinsp_fdtable(sinsp* inspector); inline sinsp_fdinfo_t* find(int64_t fd) { std::unordered_map::iterator fdit; // // Try looking up in our simple cache // if(m_last_accessed_fd != -1 && fd == m_last_accessed_fd) { #ifdef GATHER_INTERNAL_STATS m_inspector->m_stats.m_n_cached_fd_lookups++; #endif return m_last_accessed_fdinfo; } // // Caching failed, do a real lookup // fdit = m_table.find(fd); if(fdit == m_table.end()) { #ifdef GATHER_INTERNAL_STATS m_inspector->m_stats.m_n_failed_fd_lookups++; #endif return NULL; } else { #ifdef GATHER_INTERNAL_STATS m_inspector->m_stats.m_n_noncached_fd_lookups++; #endif m_last_accessed_fd = fd; m_last_accessed_fdinfo = &(fdit->second); return &(fdit->second); } } // If the key is already present, overwrite the existing value and return false. sinsp_fdinfo_t* add(int64_t fd, sinsp_fdinfo_t* fdinfo); // If the key is present, returns true, otherwise returns false. void erase(int64_t fd); void clear(); size_t size(); void reset_cache(); sinsp* m_inspector; std::unordered_map m_table; // // Simple fd cache // int64_t m_last_accessed_fd; sinsp_fdinfo_t *m_last_accessed_fdinfo; }; sysdig-0.26.4/userspace/libsinsp/filter.cpp000066400000000000000000001475441352731327100207360ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // Why isn't this parser written using antlr or some other parser generator? // Essentially, after dealing with that stuff multiple times in the past, and fighting for a day // to configure everything with crappy documentation and code that doesn't compile, // I decided that I agree with this http://mortoray.com/2012/07/20/why-i-dont-use-a-parser-generator/ // and that I'm going with a manually written parser. The grammar is simple enough that it's not // going to take more time. On the other hand I will avoid a crappy dependency that breaks my // code at every new release, and I will have a cleaner and easier to understand code base. // #ifdef _WIN32 #define NOMINMAX #endif #include #include #include "sinsp.h" #include "sinsp_int.h" #include "utils.h" #ifdef HAS_FILTERING #include "filter.h" #include "filterchecks.h" #include "value_parser.h" #ifndef _WIN32 #include "arpa/inet.h" #endif #ifndef _GNU_SOURCE // // Fallback implementation of memmem // void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); #endif #ifdef _WIN32 #pragma comment(lib, "Ws2_32.lib") #include #else #include #endif extern sinsp_filter_check_list g_filterlist; /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_list implementation /////////////////////////////////////////////////////////////////////////////// sinsp_filter_check_list::sinsp_filter_check_list() { ////////////////////////////////////////////////////////////////////////////// // ADD NEW FILTER CHECK CLASSES HERE ////////////////////////////////////////////////////////////////////////////// add_filter_check(new sinsp_filter_check_fd()); add_filter_check(new sinsp_filter_check_thread()); add_filter_check(new sinsp_filter_check_event()); add_filter_check(new sinsp_filter_check_user()); add_filter_check(new sinsp_filter_check_group()); add_filter_check(new sinsp_filter_check_syslog()); add_filter_check(new sinsp_filter_check_container()); add_filter_check(new sinsp_filter_check_utils()); add_filter_check(new sinsp_filter_check_fdlist()); #ifndef CYGWING_AGENT add_filter_check(new sinsp_filter_check_k8s()); add_filter_check(new sinsp_filter_check_mesos()); #endif add_filter_check(new sinsp_filter_check_tracer()); add_filter_check(new sinsp_filter_check_evtin()); } sinsp_filter_check_list::~sinsp_filter_check_list() { uint32_t j; for(j = 0; j < m_check_list.size(); j++) { delete m_check_list[j]; } } void sinsp_filter_check_list::add_filter_check(sinsp_filter_check* filter_check) { m_check_list.push_back(filter_check); } void sinsp_filter_check_list::get_all_fields(OUT vector* list) { uint32_t j; for(j = 0; j < m_check_list.size(); j++) { list->push_back((const filter_check_info*)&(m_check_list[j]->m_info)); } } sinsp_filter_check* sinsp_filter_check_list::new_filter_check_from_fldname(const string& name, sinsp* inspector, bool do_exact_check) { uint32_t j; for(j = 0; j < m_check_list.size(); j++) { m_check_list[j]->m_inspector = inspector; int32_t fldnamelen = m_check_list[j]->parse_field_name(name.c_str(), false, true); if(fldnamelen != -1) { if(do_exact_check) { if((int32_t)name.size() != fldnamelen) { goto field_not_found; } } sinsp_filter_check* newchk = m_check_list[j]->allocate_new(); newchk->set_inspector(inspector); return newchk; } } field_not_found: // // If you are implementing a new filter check and this point is reached, // it's very likely that you've forgotten to add your filter to the list in // the constructor // return NULL; } sinsp_filter_check* sinsp_filter_check_list::new_filter_check_from_another(sinsp_filter_check *chk) { sinsp_filter_check *newchk = chk->allocate_new(); newchk->m_inspector = chk->m_inspector; newchk->m_field_id = chk->m_field_id; newchk->m_field = &chk->m_info.m_fields[chk->m_field_id]; newchk->m_boolop = chk->m_boolop; newchk->m_cmpop = chk->m_cmpop; return newchk; } /////////////////////////////////////////////////////////////////////////////// // type-based comparison functions /////////////////////////////////////////////////////////////////////////////// bool flt_compare_uint64(cmpop op, uint64_t operand1, uint64_t operand2) { switch(op) { case CO_EQ: return (operand1 == operand2); case CO_NE: return (operand1 != operand2); case CO_LT: return (operand1 < operand2); case CO_LE: return (operand1 <= operand2); case CO_GT: return (operand1 > operand2); case CO_GE: return (operand1 >= operand2); case CO_CONTAINS: throw sinsp_exception("'contains' not supported for numeric filters"); return false; case CO_ICONTAINS: throw sinsp_exception("'icontains' not supported for numeric filters"); return false; case CO_STARTSWITH: throw sinsp_exception("'startswith' not supported for numeric filters"); return false; case CO_ENDSWITH: throw sinsp_exception("'endswith' not supported for numeric filters"); return false; case CO_GLOB: throw sinsp_exception("'glob' not supported for numeric filters"); return false; default: throw sinsp_exception("'unknown' not supported for numeric filters"); return false; } } bool flt_compare_int64(cmpop op, int64_t operand1, int64_t operand2) { switch(op) { case CO_EQ: return (operand1 == operand2); case CO_NE: return (operand1 != operand2); case CO_LT: return (operand1 < operand2); case CO_LE: return (operand1 <= operand2); case CO_GT: return (operand1 > operand2); case CO_GE: return (operand1 >= operand2); case CO_CONTAINS: throw sinsp_exception("'contains' not supported for numeric filters"); return false; case CO_ICONTAINS: throw sinsp_exception("'icontains' not supported for numeric filters"); return false; case CO_STARTSWITH: throw sinsp_exception("'startswith' not supported for numeric filters"); return false; case CO_ENDSWITH: throw sinsp_exception("'endswith' not supported for numeric filters"); return false; case CO_GLOB: throw sinsp_exception("'glob' not supported for numeric filters"); return false; default: throw sinsp_exception("'unknown' not supported for numeric filters"); return false; } } bool flt_compare_string(cmpop op, char* operand1, char* operand2) { switch(op) { case CO_EQ: return (strcmp(operand1, operand2) == 0); case CO_NE: return (strcmp(operand1, operand2) != 0); case CO_CONTAINS: return (strstr(operand1, operand2) != NULL); case CO_ICONTAINS: #ifdef _WIN32 return (_strnicmp(operand1, operand2, strlen(operand1)) != NULL); #else return (strcasestr(operand1, operand2) != NULL); #endif case CO_STARTSWITH: return (strncmp(operand1, operand2, strlen(operand2)) == 0); case CO_ENDSWITH: return (sinsp_utils::endswith(operand1, operand2)); case CO_GLOB: return sinsp_utils::glob_match(operand2, operand1); case CO_LT: return (strcmp(operand1, operand2) < 0); case CO_LE: return (strcmp(operand1, operand2) <= 0); case CO_GT: return (strcmp(operand1, operand2) > 0); case CO_GE: return (strcmp(operand1, operand2) >= 0); default: ASSERT(false); throw sinsp_exception("invalid filter operator " + std::to_string((long long) op)); return false; } } bool flt_compare_buffer(cmpop op, char* operand1, char* operand2, uint32_t op1_len, uint32_t op2_len) { switch(op) { case CO_EQ: return op1_len == op2_len && (memcmp(operand1, operand2, op1_len) == 0); case CO_NE: return op1_len != op2_len || (memcmp(operand1, operand2, op1_len) != 0); case CO_CONTAINS: return (memmem(operand1, op1_len, operand2, op2_len) != NULL); case CO_ICONTAINS: throw sinsp_exception("'icontains' not supported for buffer filters"); case CO_STARTSWITH: return (memcmp(operand1, operand2, op2_len) == 0); case CO_ENDSWITH: return (sinsp_utils::endswith(operand1, operand2, op1_len, op2_len)); case CO_GLOB: throw sinsp_exception("'glob' not supported for buffer filters"); case CO_LT: throw sinsp_exception("'<' not supported for buffer filters"); case CO_LE: throw sinsp_exception("'<=' not supported for buffer filters"); case CO_GT: throw sinsp_exception("'>' not supported for buffer filters"); case CO_GE: throw sinsp_exception("'>=' not supported for buffer filters"); default: ASSERT(false); throw sinsp_exception("invalid filter operator " + std::to_string((long long) op)); return false; } } bool flt_compare_double(cmpop op, double operand1, double operand2) { switch(op) { case CO_EQ: return (operand1 == operand2); case CO_NE: return (operand1 != operand2); case CO_LT: return (operand1 < operand2); case CO_LE: return (operand1 <= operand2); case CO_GT: return (operand1 > operand2); case CO_GE: return (operand1 >= operand2); case CO_CONTAINS: throw sinsp_exception("'contains' not supported for numeric filters"); return false; case CO_ICONTAINS: throw sinsp_exception("'icontains' not supported for numeric filters"); return false; case CO_STARTSWITH: throw sinsp_exception("'startswith' not supported for numeric filters"); return false; case CO_ENDSWITH: throw sinsp_exception("'endswith' not supported for numeric filters"); return false; case CO_GLOB: throw sinsp_exception("'glob' not supported for numeric filters"); return false; default: throw sinsp_exception("'unknown' not supported for numeric filters"); return false; } } bool flt_compare_ipv4net(cmpop op, uint64_t operand1, ipv4net* operand2) { switch(op) { case CO_EQ: case CO_IN: { return ((operand1 & operand2->m_netmask) == (operand2->m_ip & operand2->m_netmask)); } case CO_NE: return ((operand1 & operand2->m_netmask) != (operand2->m_ip & operand2->m_netmask)); case CO_CONTAINS: throw sinsp_exception("'contains' not supported for numeric filters"); return false; case CO_ICONTAINS: throw sinsp_exception("'icontains' not supported for numeric filters"); return false; case CO_STARTSWITH: throw sinsp_exception("'startswith' not supported for numeric filters"); return false; case CO_ENDSWITH: throw sinsp_exception("'endswith' not supported for numeric filters"); return false; case CO_GLOB: throw sinsp_exception("'glob' not supported for numeric filters"); return false; default: throw sinsp_exception("comparison operator not supported for ipv4 networks"); } } bool flt_compare_ipv6addr(cmpop op, ipv6addr *operand1, ipv6addr *operand2) { switch(op) { case CO_EQ: case CO_IN: return *operand1 == *operand2; case CO_NE: return *operand1 != *operand2; case CO_CONTAINS: throw sinsp_exception("'contains' not supported for ipv6 addresses"); return false; case CO_ICONTAINS: throw sinsp_exception("'icontains' not supported for ipv6 addresses"); return false; case CO_STARTSWITH: throw sinsp_exception("'startswith' not supported for ipv6 addresses"); return false; case CO_GLOB: throw sinsp_exception("'glob' not supported for ipv6 addresses"); return false; default: throw sinsp_exception("comparison operator not supported for ipv6 addresses"); } } bool flt_compare_ipv6net(cmpop op, ipv6addr *operand1, ipv6addr *operand2) { switch(op) { case CO_EQ: case CO_IN: return operand1->in_subnet(*operand2); case CO_NE: return !operand1->in_subnet(*operand2); case CO_CONTAINS: throw sinsp_exception("'contains' not supported for ipv6 networks"); return false; case CO_ICONTAINS: throw sinsp_exception("'icontains' not supported for ipv6 networks"); return false; case CO_STARTSWITH: throw sinsp_exception("'startswith' not supported for ipv6 networks"); return false; case CO_GLOB: throw sinsp_exception("'glob' not supported for ipv6 networks"); return false; default: throw sinsp_exception("comparison operator not supported for ipv6 networks"); } } bool flt_compare(cmpop op, ppm_param_type type, void* operand1, void* operand2, uint32_t op1_len, uint32_t op2_len) { // // sinsp_filter_check_*::compare // already discard NULL values // if(op == CO_EXISTS) { return true; } switch(type) { case PT_INT8: return flt_compare_int64(op, (int64_t)*(int8_t*)operand1, (int64_t)*(int8_t*)operand2); case PT_INT16: return flt_compare_int64(op, (int64_t)*(int16_t*)operand1, (int64_t)*(int16_t*)operand2); case PT_INT32: return flt_compare_int64(op, (int64_t)*(int32_t*)operand1, (int64_t)*(int32_t*)operand2); case PT_INT64: case PT_FD: case PT_PID: case PT_ERRNO: return flt_compare_int64(op, *(int64_t*)operand1, *(int64_t*)operand2); case PT_FLAGS8: case PT_UINT8: case PT_SIGTYPE: return flt_compare_uint64(op, (uint64_t)*(uint8_t*)operand1, (uint64_t)*(uint8_t*)operand2); case PT_FLAGS16: case PT_UINT16: case PT_PORT: case PT_SYSCALLID: return flt_compare_uint64(op, (uint64_t)*(uint16_t*)operand1, (uint64_t)*(uint16_t*)operand2); case PT_UINT32: case PT_FLAGS32: case PT_MODE: case PT_BOOL: case PT_IPV4ADDR: return flt_compare_uint64(op, (uint64_t)*(uint32_t*)operand1, (uint64_t)*(uint32_t*)operand2); case PT_IPV4NET: return flt_compare_ipv4net(op, (uint64_t)*(uint32_t*)operand1, (ipv4net*)operand2); case PT_IPV6ADDR: return flt_compare_ipv6addr(op, (ipv6addr *)operand1, (ipv6addr *)operand2); case PT_IPV6NET: return flt_compare_ipv6net(op, (ipv6addr *)operand1, (ipv6addr*)operand2); case PT_IPADDR: if(op1_len == sizeof(struct in_addr)) { return flt_compare(op, PT_IPV4ADDR, operand1, operand2, op1_len, op2_len); } else if(op1_len == sizeof(struct in6_addr)) { return flt_compare(op, PT_IPV6ADDR, operand1, operand2, op1_len, op2_len); } else { throw sinsp_exception("rawval_to_string called with IP address of incorrect size " + to_string(op1_len)); } case PT_IPNET: if(op1_len == sizeof(struct in_addr)) { return flt_compare(op, PT_IPV4NET, operand1, operand2, op1_len, op2_len); } else if(op1_len == sizeof(struct in6_addr)) { return flt_compare(op, PT_IPV6NET, operand1, operand2, op1_len, op2_len); } else { throw sinsp_exception("rawval_to_string called with IP network of incorrect size " + to_string(op1_len)); } case PT_UINT64: case PT_RELTIME: case PT_ABSTIME: return flt_compare_uint64(op, *(uint64_t*)operand1, *(uint64_t*)operand2); case PT_CHARBUF: return flt_compare_string(op, (char*)operand1, (char*)operand2); case PT_BYTEBUF: return flt_compare_buffer(op, (char*)operand1, (char*)operand2, op1_len, op2_len); case PT_DOUBLE: return flt_compare_double(op, *(double*)operand1, *(double*)operand2); case PT_SOCKADDR: case PT_SOCKTUPLE: case PT_FDLIST: case PT_FSPATH: case PT_SIGSET: default: ASSERT(false); return false; } } bool flt_compare_avg(cmpop op, ppm_param_type type, void* operand1, void* operand2, uint32_t op1_len, uint32_t op2_len, uint32_t cnt1, uint32_t cnt2) { int64_t i641, i642; uint64_t u641, u642; double d1, d2; // // If count = 0 we assume that the value is zero too (there are assertions to // check that, and we just divide by 1 // if(cnt1 == 0) { cnt1 = 1; } if(cnt2 == 0) { cnt2 = 1; } switch(type) { case PT_INT8: i641 = ((int64_t)*(int8_t*)operand1) / cnt1; i642 = ((int64_t)*(int8_t*)operand2) / cnt2; ASSERT(cnt1 != 0 || i641 == 0); ASSERT(cnt2 != 0 || i642 == 0); return flt_compare_int64(op, i641, i642); case PT_INT16: i641 = ((int64_t)*(int16_t*)operand1) / cnt1; i642 = ((int64_t)*(int16_t*)operand2) / cnt2; ASSERT(cnt1 != 0 || i641 == 0); ASSERT(cnt2 != 0 || i642 == 0); return flt_compare_int64(op, i641, i642); case PT_INT32: i641 = ((int64_t)*(int32_t*)operand1) / cnt1; i642 = ((int64_t)*(int32_t*)operand2) / cnt2; ASSERT(cnt1 != 0 || i641 == 0); ASSERT(cnt2 != 0 || i642 == 0); return flt_compare_int64(op, i641, i642); case PT_INT64: case PT_FD: case PT_PID: case PT_ERRNO: i641 = ((int64_t)*(int64_t*)operand1) / cnt1; i642 = ((int64_t)*(int64_t*)operand2) / cnt2; ASSERT(cnt1 != 0 || i641 == 0); ASSERT(cnt2 != 0 || i642 == 0); return flt_compare_int64(op, i641, i642); case PT_FLAGS8: case PT_UINT8: case PT_SIGTYPE: u641 = ((uint64_t)*(uint8_t*)operand1) / cnt1; u642 = ((uint64_t)*(uint8_t*)operand2) / cnt2; ASSERT(cnt1 != 0 || u641 == 0); ASSERT(cnt2 != 0 || u642 == 0); return flt_compare_uint64(op, u641, u642); case PT_FLAGS16: case PT_UINT16: case PT_PORT: case PT_SYSCALLID: u641 = ((uint64_t)*(uint16_t*)operand1) / cnt1; u642 = ((uint64_t)*(uint16_t*)operand2) / cnt2; ASSERT(cnt1 != 0 || u641 == 0); ASSERT(cnt2 != 0 || u642 == 0); return flt_compare_uint64(op, u641, u642); case PT_UINT32: case PT_FLAGS32: case PT_MODE: case PT_BOOL: case PT_IPV4ADDR: case PT_IPV6ADDR: // What does an average mean for ip addresses anyway? u641 = ((uint64_t)*(uint32_t*)operand1) / cnt1; u642 = ((uint64_t)*(uint32_t*)operand2) / cnt2; ASSERT(cnt1 != 0 || u641 == 0); ASSERT(cnt2 != 0 || u642 == 0); return flt_compare_uint64(op, u641, u642); case PT_UINT64: case PT_RELTIME: case PT_ABSTIME: u641 = (*(uint64_t*)operand1) / cnt1; u642 = (*(uint64_t*)operand2) / cnt2; ASSERT(cnt1 != 0 || u641 == 0); ASSERT(cnt2 != 0 || u642 == 0); return flt_compare_uint64(op, u641, u642); case PT_DOUBLE: d1 = (*(double*)operand1) / cnt1; d2 = (*(double*)operand2) / cnt2; ASSERT(cnt1 != 0 || d1 == 0); ASSERT(cnt2 != 0 || d2 == 0); return flt_compare_double(op, d1, d2); default: ASSERT(false); return false; } } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check implementation /////////////////////////////////////////////////////////////////////////////// sinsp_filter_check::sinsp_filter_check() { m_boolop = BO_NONE; m_cmpop = CO_NONE; m_inspector = NULL; m_field = NULL; m_info.m_fields = NULL; m_info.m_nfields = -1; m_val_storage_len = 0; m_aggregation = A_NONE; m_merge_aggregation = A_NONE; m_val_storages = vector> (1, vector(256)); m_val_storages_min_size = (numeric_limits::max)(); m_val_storages_max_size = (numeric_limits::min)(); } void sinsp_filter_check::set_inspector(sinsp* inspector) { m_inspector = inspector; } Json::Value sinsp_filter_check::rawval_to_json(uint8_t* rawval, ppm_param_type ptype, ppm_print_format print_format, uint32_t len) { ASSERT(rawval != NULL); switch(ptype) { case PT_INT8: if(print_format == PF_DEC || print_format == PF_ID) { return *(int8_t *)rawval; } else if(print_format == PF_OCT || print_format == PF_HEX) { return rawval_to_string(rawval, ptype, print_format, len); } else { ASSERT(false); return Json::nullValue; } case PT_INT16: if(print_format == PF_DEC || print_format == PF_ID) { return *(int16_t *)rawval; } else if(print_format == PF_OCT || print_format == PF_HEX) { return rawval_to_string(rawval, ptype, print_format, len); } else { ASSERT(false); return Json::nullValue; } case PT_INT32: if(print_format == PF_DEC || print_format == PF_ID) { return *(int32_t *)rawval; } else if(print_format == PF_OCT || print_format == PF_HEX) { return rawval_to_string(rawval, ptype, print_format, len); } else { ASSERT(false); return Json::nullValue; } case PT_INT64: case PT_PID: if(print_format == PF_DEC || print_format == PF_ID) { return (Json::Value::Int64)*(int64_t *)rawval; } else { return rawval_to_string(rawval, ptype, print_format, len); } case PT_L4PROTO: // This can be resolved in the future case PT_UINT8: if(print_format == PF_DEC || print_format == PF_ID) { return *(uint8_t *)rawval; } else if(print_format == PF_OCT || print_format == PF_HEX) { return rawval_to_string(rawval, ptype, print_format, len); } else { ASSERT(false); return Json::nullValue; } case PT_PORT: // This can be resolved in the future case PT_UINT16: if(print_format == PF_DEC || print_format == PF_ID) { return *(uint16_t *)rawval; } else if(print_format == PF_OCT || print_format == PF_HEX) { return rawval_to_string(rawval, ptype, print_format, len); } else { ASSERT(false); return Json::nullValue; } case PT_UINT32: if(print_format == PF_DEC || print_format == PF_ID) { return *(uint32_t *)rawval; } else if(print_format == PF_OCT || print_format == PF_HEX) { return rawval_to_string(rawval, ptype, print_format, len); } else { ASSERT(false); return Json::nullValue; } case PT_UINT64: case PT_RELTIME: case PT_ABSTIME: if(print_format == PF_DEC || print_format == PF_ID) { return (Json::Value::UInt64)*(uint64_t *)rawval; } else if( print_format == PF_10_PADDED_DEC || print_format == PF_OCT || print_format == PF_HEX) { return rawval_to_string(rawval, ptype, print_format, len); } else { ASSERT(false); return Json::nullValue; } case PT_SOCKADDR: case PT_SOCKFAMILY: ASSERT(false); return Json::nullValue; case PT_BOOL: return Json::Value((bool)(*(uint32_t*)rawval != 0)); case PT_CHARBUF: case PT_FSPATH: case PT_BYTEBUF: case PT_IPV4ADDR: case PT_IPV6ADDR: case PT_IPADDR: return rawval_to_string(rawval, ptype, print_format, len); default: ASSERT(false); throw sinsp_exception("wrong event type " + to_string((long long) ptype)); } } char* sinsp_filter_check::rawval_to_string(uint8_t* rawval, ppm_param_type ptype, ppm_print_format print_format, uint32_t len) { char* prfmt; ASSERT(rawval != NULL); switch(ptype) { case PT_INT8: if(print_format == PF_OCT) { prfmt = (char*)"%" PRIo8; } else if(print_format == PF_DEC || print_format == PF_ID) { prfmt = (char*)"%" PRId8; } else if(print_format == PF_HEX) { prfmt = (char*)"%" PRIX8; } else { ASSERT(false); return NULL; } snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), prfmt, *(int8_t *)rawval); return m_getpropertystr_storage; case PT_INT16: if(print_format == PF_OCT) { prfmt = (char*)"%" PRIo16; } else if(print_format == PF_DEC || print_format == PF_ID) { prfmt = (char*)"%" PRId16; } else if(print_format == PF_HEX) { prfmt = (char*)"%" PRIX16; } else { ASSERT(false); return NULL; } snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), prfmt, *(int16_t *)rawval); return m_getpropertystr_storage; case PT_INT32: if(print_format == PF_OCT) { prfmt = (char*)"%" PRIo32; } else if(print_format == PF_DEC || print_format == PF_ID) { prfmt = (char*)"%" PRId32; } else if(print_format == PF_HEX) { prfmt = (char*)"%" PRIX32; } else { ASSERT(false); return NULL; } snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), prfmt, *(int32_t *)rawval); return m_getpropertystr_storage; case PT_INT64: case PT_PID: case PT_ERRNO: if(print_format == PF_OCT) { prfmt = (char*)"%" PRIo64; } else if(print_format == PF_DEC || print_format == PF_ID) { prfmt = (char*)"%" PRId64; } else if(print_format == PF_10_PADDED_DEC) { prfmt = (char*)"%09" PRId64; } else if(print_format == PF_HEX) { prfmt = (char*)"%" PRIX64; } else { prfmt = (char*)"%" PRId64; } snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), prfmt, *(int64_t *)rawval); return m_getpropertystr_storage; case PT_L4PROTO: // This can be resolved in the future case PT_UINT8: if(print_format == PF_OCT) { prfmt = (char*)"%" PRIo8; } else if(print_format == PF_DEC || print_format == PF_ID) { prfmt = (char*)"%" PRIu8; } else if(print_format == PF_HEX) { prfmt = (char*)"%" PRIu8; } else { ASSERT(false); return NULL; } snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), prfmt, *(uint8_t *)rawval); return m_getpropertystr_storage; case PT_PORT: // This can be resolved in the future case PT_UINT16: if(print_format == PF_OCT) { prfmt = (char*)"%" PRIo16; } else if(print_format == PF_DEC || print_format == PF_ID) { prfmt = (char*)"%" PRIu16; } else if(print_format == PF_HEX) { prfmt = (char*)"%" PRIu16; } else { ASSERT(false); return NULL; } snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), prfmt, *(uint16_t *)rawval); return m_getpropertystr_storage; case PT_UINT32: if(print_format == PF_OCT) { prfmt = (char*)"%" PRIo32; } else if(print_format == PF_DEC || print_format == PF_ID) { prfmt = (char*)"%" PRIu32; } else if(print_format == PF_HEX) { prfmt = (char*)"%" PRIu32; } else { ASSERT(false); return NULL; } snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), prfmt, *(uint32_t *)rawval); return m_getpropertystr_storage; case PT_UINT64: case PT_RELTIME: case PT_ABSTIME: if(print_format == PF_OCT) { prfmt = (char*)"%" PRIo64; } else if(print_format == PF_DEC || print_format == PF_ID) { prfmt = (char*)"%" PRIu64; } else if(print_format == PF_10_PADDED_DEC) { prfmt = (char*)"%09" PRIu64; } else if(print_format == PF_HEX) { prfmt = (char*)"%" PRIX64; } else { ASSERT(false); return NULL; } snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), prfmt, *(uint64_t *)rawval); return m_getpropertystr_storage; case PT_CHARBUF: case PT_FSPATH: return (char*)rawval; case PT_BYTEBUF: if(rawval[len] == 0) { return (char*)rawval; } else { ASSERT(len < 1024 * 1024); if(len >= filter_value().size()) { filter_value().resize(len + 1); } memcpy(filter_value_p(), rawval, len); filter_value_p()[len] = 0; return (char*)filter_value_p(); } case PT_SOCKADDR: ASSERT(false); return NULL; case PT_SOCKFAMILY: ASSERT(false); return NULL; case PT_BOOL: if(*(uint32_t*)rawval != 0) { return (char*)"true"; } else { return (char*)"false"; } case PT_IPV4ADDR: snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8, rawval[0], rawval[1], rawval[2], rawval[3]); return m_getpropertystr_storage; case PT_IPV6ADDR: { char address[100]; if(NULL == inet_ntop(AF_INET6, rawval, address, 100)) { strcpy(address, ""); } strncpy(m_getpropertystr_storage, address, 100); return m_getpropertystr_storage; } case PT_IPADDR: if(len == sizeof(struct in_addr)) { return rawval_to_string(rawval, PT_IPV4ADDR, print_format, len); } else if(len == sizeof(struct in6_addr)) { return rawval_to_string(rawval, PT_IPV6ADDR, print_format, len); } else { throw sinsp_exception("rawval_to_string called with IP address of incorrect size " + to_string(len)); } case PT_DOUBLE: snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), "%.1lf", *(double*)rawval); return m_getpropertystr_storage; default: ASSERT(false); throw sinsp_exception("wrong event type " + to_string((long long) ptype)); } } char* sinsp_filter_check::tostring(sinsp_evt* evt) { uint32_t len; uint8_t* rawval = extract(evt, &len); if(rawval == NULL) { return NULL; } return rawval_to_string(rawval, m_field->m_type, m_field->m_print_format, len); } Json::Value sinsp_filter_check::tojson(sinsp_evt* evt) { uint32_t len; Json::Value jsonval = extract_as_js(evt, &len); if(jsonval == Json::nullValue) { uint8_t* rawval = extract(evt, &len); if(rawval == NULL) { return Json::nullValue; } return rawval_to_json(rawval, m_field->m_type, m_field->m_print_format, len); } return jsonval; } int32_t sinsp_filter_check::parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) { int32_t j; int32_t max_fldlen = -1; uint32_t max_flags = 0; ASSERT(m_info.m_fields != NULL); ASSERT(m_info.m_nfields != -1); string val(str); m_field_id = 0xffffffff; for(j = 0; j < m_info.m_nfields; j++) { string fldname = m_info.m_fields[j].m_name; int32_t fldlen = (uint32_t)fldname.length(); if(val.compare(0, fldlen, fldname) == 0) { if(fldlen > max_fldlen) { m_field_id = j; m_field = &m_info.m_fields[j]; max_fldlen = fldlen; max_flags = (m_info.m_fields[j]).m_flags; } } } if(!needed_for_filtering) { if(max_flags & EPF_FILTER_ONLY) { throw sinsp_exception(string(str) + " is filter only and cannot be used as a display field"); } } return max_fldlen; } void sinsp_filter_check::add_filter_value(const char* str, uint32_t len, uint32_t i) { size_t parsed_len; if (i >= m_val_storages.size()) { m_val_storages.push_back(vector(256)); } parsed_len = parse_filter_value(str, len, filter_value_p(i), filter_value(i).size()); // XXX/mstemm this doesn't work if someone called // add_filter_value more than once for a given index. filter_value_t item(filter_value_p(i), parsed_len); m_val_storages_members.insert(item); if(parsed_len < m_val_storages_min_size) { m_val_storages_min_size = parsed_len; } if(parsed_len > m_val_storages_max_size) { m_val_storages_max_size = parsed_len; } // If the operator is CO_PMATCH, also add the value to the paths set. if (m_cmpop == CO_PMATCH) { m_val_storages_paths.add_search_path(item); } } size_t sinsp_filter_check::parse_filter_value(const char* str, uint32_t len, uint8_t *storage, uint32_t storage_len) { size_t parsed_len; // byte buffer, no parsing needed if (m_field->m_type == PT_BYTEBUF) { if(len >= storage_len) { throw sinsp_exception("filter parameter too long:" + string(str)); } memcpy(storage, str, len); m_val_storage_len = len; return len; } else { parsed_len = sinsp_filter_value_parser::string_to_rawval(str, len, storage, storage_len, m_field->m_type); } validate_filter_value(str, len); return parsed_len; } const filtercheck_field_info* sinsp_filter_check::get_field_info() { return &m_info.m_fields[m_field_id]; } bool sinsp_filter_check::flt_compare(cmpop op, ppm_param_type type, void* operand1, uint32_t op1_len, uint32_t op2_len) { if (op == CO_IN || op == CO_PMATCH) { // Certain filterchecks can't be done as a set // membership test/group match. For these, just loop over the // values and see if any value is equal. switch(type) { case PT_IPV4NET: case PT_IPV6NET: case PT_IPNET: case PT_SOCKADDR: case PT_SOCKTUPLE: case PT_FDLIST: case PT_FSPATH: case PT_SIGSET: for (uint16_t i=0; i < m_val_storages.size(); i++) { if (::flt_compare(CO_EQ, type, operand1, filter_value_p(i), op1_len, filter_value(i).size())) { return true; } } return false; break; default: // For raw strings, the length may not be set. So we do a strlen to find it. if(type == PT_CHARBUF && op1_len == 0) { op1_len = strlen((char *) operand1); } filter_value_t item((uint8_t *) operand1, op1_len); if (op == CO_IN) { if(op1_len >= m_val_storages_min_size && op1_len <= m_val_storages_max_size && m_val_storages_members.find(item) != m_val_storages_members.end()) { return true; } } else { if (m_val_storages_paths.match(item)) { return true; } } return false; break; } } else { return (::flt_compare(op, type, operand1, filter_value_p(), op1_len, op2_len) ); } } uint8_t* sinsp_filter_check::extract(gen_event *evt, OUT uint32_t* len, bool sanitize_strings) { return extract((sinsp_evt *) evt, len, sanitize_strings); } bool sinsp_filter_check::compare(gen_event *evt) { return compare((sinsp_evt *) evt); } bool sinsp_filter_check::compare(sinsp_evt *evt) { uint32_t evt_val_len=0; bool sanitize_strings = false; uint8_t* extracted_val = extract(evt, &evt_val_len, sanitize_strings); if(extracted_val == NULL) { return false; } return flt_compare(m_cmpop, m_info.m_fields[m_field_id].m_type, extracted_val, evt_val_len, m_val_storage_len); } sinsp_filter::sinsp_filter(sinsp *inspector) { m_inspector = inspector; } sinsp_filter::~sinsp_filter() { } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_compiler implementation /////////////////////////////////////////////////////////////////////////////// sinsp_filter_compiler::sinsp_filter_compiler(sinsp* inspector, const string& fltstr, bool ttable_only) { m_inspector = inspector; m_ttable_only = ttable_only; m_scanpos = -1; m_scansize = 0; m_state = ST_NEED_EXPRESSION; m_filter = new sinsp_filter(m_inspector); m_last_boolop = BO_NONE; m_nest_level = 0; m_fltstr = fltstr; } sinsp_filter_compiler::~sinsp_filter_compiler() { } bool sinsp_filter_compiler::isblank(char c) { if(c == ' ' || c == '\t' || c == '\n' || c == '\r') { return true; } else { return false; } } bool sinsp_filter_compiler::is_special_char(char c) { if(c == '(' || c == ')' || c == '!' || c == '=' || c == '<' || c == '>') { return true; } return false; } bool sinsp_filter_compiler::is_bracket(char c) { if(c == '(' || c == ')') { return true; } return false; } char sinsp_filter_compiler::next() { while(true) { m_scanpos++; if(m_scanpos >= m_scansize) { return 0; } if(!isblank(m_fltstr[m_scanpos])) { return m_fltstr[m_scanpos]; } } } vector sinsp_filter_compiler::next_operand(bool expecting_first_operand, bool in_or_pmatch_clause) { vector res; bool is_quoted = false; int32_t start; int32_t nums[2]; uint32_t num_pos; enum ppm_escape_state { PES_NORMAL, PES_SLASH, PES_NUMBER, PES_ERROR, } escape_state; // // Skip spaces // if(isblank(m_fltstr[m_scanpos])) { next(); } // // If there are quotes, don't stop on blank // if(m_scanpos < m_scansize && (m_fltstr[m_scanpos] == '"' || m_fltstr[m_scanpos] == '\'')) { is_quoted = true; m_scanpos++; } // // Mark the beginning of the word // start = m_scanpos; escape_state = PES_NORMAL; num_pos = 0; while(m_scanpos < m_scansize && escape_state != PES_ERROR) { char curchar = m_fltstr[m_scanpos]; bool is_end_of_word; if(expecting_first_operand) { is_end_of_word = (isblank(curchar) || is_special_char(curchar)); } else { is_end_of_word = (!is_quoted && (isblank(curchar) || is_bracket(curchar) || (in_or_pmatch_clause && curchar == ','))) || (is_quoted && escape_state != PES_SLASH && (curchar == '"' || curchar == '\'')); } if(is_end_of_word) { if(escape_state != PES_NORMAL) { escape_state = PES_ERROR; break; } // // End of word // ASSERT(m_scanpos >= start); if(curchar == '(' || curchar == ')' || (in_or_pmatch_clause && curchar == ',')) { m_scanpos--; } res.push_back('\0'); return res; } switch(escape_state) { case PES_NORMAL: if(curchar == '\\' && !expecting_first_operand) { escape_state = PES_SLASH; } else { res.push_back(curchar); } break; case PES_SLASH: switch(curchar) { case '\\': case '"': escape_state = PES_NORMAL; res.push_back(curchar); break; case 'x': escape_state = PES_NUMBER; break; default: escape_state = PES_NORMAL; res.push_back('\\'); res.push_back(curchar); break; } break; case PES_NUMBER: if(isdigit((int)curchar)) { nums[num_pos++] = curchar - '0'; } else if((curchar >= 'a' && curchar <= 'f') || (curchar >= 'A' && curchar <= 'F')) { nums[num_pos++] = tolower((int)curchar) - 'a' + 10; } else { escape_state = PES_ERROR; } if(num_pos == 2 && escape_state != PES_ERROR) { res.push_back((char)(nums[0] * 16 + nums[1])); num_pos = 0; escape_state = PES_NORMAL; } break; default: ASSERT(false); escape_state = PES_ERROR; break; } m_scanpos++; } if(escape_state == PES_ERROR) { throw sinsp_exception("filter error: unrecognized escape sequence at " + m_fltstr.substr(start, m_scanpos)); } else if(is_quoted) { throw sinsp_exception("filter error: unclosed quotes"); } // // End of filter // res.push_back('\0'); return res; } bool sinsp_filter_compiler::compare_no_consume(const string& str) { // // If the rest of the filter cannot contain the operand we may return // The filter may finish with the operand itself though (e.g. "proc.name exists") // if(m_scanpos + (int32_t)str.size() > m_scansize) { return false; } string tstr = m_fltstr.substr(m_scanpos, str.size()); if(tstr == str) { return true; } else { return false; } } cmpop sinsp_filter_compiler::next_comparison_operator() { int32_t start; // // Skip spaces // if(isblank(m_fltstr[m_scanpos])) { next(); } // // Mark the beginning of the word // start = m_scanpos; if(compare_no_consume("=")) { m_scanpos += 1; return CO_EQ; } else if(compare_no_consume("!=")) { m_scanpos += 2; return CO_NE; } else if(compare_no_consume("<=")) { m_scanpos += 2; return CO_LE; } else if(compare_no_consume("<")) { m_scanpos += 1; return CO_LT; } else if(compare_no_consume(">=")) { m_scanpos += 2; return CO_GE; } else if(compare_no_consume(">")) { m_scanpos += 1; return CO_GT; } else if(compare_no_consume("contains")) { m_scanpos += 8; return CO_CONTAINS; } else if(compare_no_consume("icontains")) { m_scanpos += 9; return CO_ICONTAINS; } else if(compare_no_consume("startswith")) { m_scanpos += 10; return CO_STARTSWITH; } else if(compare_no_consume("endswith")) { m_scanpos += 8; return CO_ENDSWITH; } else if(compare_no_consume("glob")) { m_scanpos += 4; return CO_GLOB; } else if(compare_no_consume("in")) { m_scanpos += 2; return CO_IN; } else if(compare_no_consume("pmatch")) { m_scanpos += 6; return CO_PMATCH; } else if(compare_no_consume("exists")) { m_scanpos += 6; return CO_EXISTS; } else { throw sinsp_exception("filter error: unrecognized comparison operator after " + m_fltstr.substr(0, start)); } } void sinsp_filter_compiler::parse_check() { uint32_t startpos = m_scanpos; vector operand1 = next_operand(true, false); string str_operand1 = string((char *)&operand1[0]); sinsp_filter_check* chk = g_filterlist.new_filter_check_from_fldname(str_operand1, m_inspector, true); boolop op = m_last_boolop; if(chk == NULL) { throw sinsp_exception("filter error: unrecognized field " + str_operand1 + " at pos " + to_string((long long) startpos)); } if(m_ttable_only) { if(!(chk->get_fields()->m_flags & filter_check_info::FL_WORKS_ON_THREAD_TABLE)) { if(str_operand1 != "evt.rawtime" && str_operand1 != "evt.rawtime.s" && str_operand1 != "evt.rawtime.ns" && str_operand1 != "evt.time" && str_operand1 != "evt.time.s" && str_operand1 != "evt.datetime" && str_operand1 != "evt.reltime") { throw sinsp_exception("the given filter is not supported for thread table filtering"); } } } cmpop co = next_comparison_operator(); chk->m_boolop = op; chk->m_cmpop = co; chk->parse_field_name((char *)&operand1[0], true, true); if(co == CO_IN || co == CO_PMATCH) { // // Skip spaces // if(isblank(m_fltstr[m_scanpos])) { next(); } if(m_fltstr[m_scanpos] != '(') { throw sinsp_exception("expected '(' after 'in/pmatch' operand"); } // // Skip '(' // m_scanpos++; if(chk->get_field_info()->m_type == PT_CHARBUF) { // // For character buffers, we can check all // values at once by putting them in a set and // checking for set membership. // // // Create the 'or' sequence // uint32_t num_values = 0; while(true) { // 'in' clause aware vector operand2 = next_operand(false, true); chk->add_filter_value((char *)&operand2[0], (uint32_t)operand2.size() - 1, num_values); num_values++; next(); if(m_fltstr[m_scanpos] == ')') { break; } else if(m_fltstr[m_scanpos] == ',') { m_scanpos++; } else { throw sinsp_exception("expected either ')' or ',' after a value inside the 'in/pmatch' clause"); } } m_filter->add_check(chk); } else if (co == CO_PMATCH) { // the pmatch operator can only work on charbufs throw sinsp_exception("pmatch requires all charbuf arguments"); } else { // // In this case we need to create '(field=value1 or field=value2 ...)' // // // Separate the 'or's from the // rest of the conditions // m_filter->push_expression(op); m_last_boolop = BO_NONE; m_nest_level++; // // The first boolean operand will be BO_NONE // Then we will start putting BO_ORs // op = BO_NONE; // // Create the 'or' sequence // while(true) { // 'in' clause aware vector operand2 = next_operand(false, true); // // Append every sinsp_filter_check creating the 'or' sequence // sinsp_filter_check* newchk = g_filterlist.new_filter_check_from_another(chk); newchk->m_boolop = op; newchk->m_cmpop = CO_EQ; newchk->add_filter_value((char *)&operand2[0], (uint32_t)operand2.size() - 1); m_filter->add_check(newchk); next(); if(m_fltstr[m_scanpos] == ')') { break; } else if(m_fltstr[m_scanpos] == ',') { m_scanpos++; } else { throw sinsp_exception("expected either ')' or ',' after a value inside the 'in' clause"); } // // From now on we 'or' every newchk // op = BO_OR; } // // Come back to the rest of the filter // m_filter->pop_expression(); m_nest_level--; } } else { // // In this case we want next() to return the very next character // At this moment m_scanpos is already at it // e.g. "(field exists) and ..." // if(co == CO_EXISTS) { m_scanpos--; } // // Otherwise we need a value for the operand // else { vector operand2 = next_operand(false, false); chk->add_filter_value((char *)&operand2[0], (uint32_t)operand2.size() - 1); } m_filter->add_check(chk); } } sinsp_filter* sinsp_filter_compiler::compile() { try { return compile_(); } catch(sinsp_exception& e) { delete m_filter; throw e; } catch(...) { delete m_filter; throw sinsp_exception("error parsing the filter string"); } } sinsp_filter* sinsp_filter_compiler::compile_() { m_scansize = (uint32_t)m_fltstr.size(); while(true) { char a = next(); switch(a) { case 0: // // Finished parsing the filter string // if(m_nest_level != 0) { throw sinsp_exception("filter error: unexpected end of filter"); } if(m_state != ST_EXPRESSION_DONE) { throw sinsp_exception("filter error: unexpected end of filter at position " + to_string((long long) m_scanpos)); } // // Good filter // return m_filter; break; case '(': if(m_state != ST_NEED_EXPRESSION) { throw sinsp_exception("unexpected '(' after " + m_fltstr.substr(0, m_scanpos)); } m_filter->push_expression(m_last_boolop); m_last_boolop = BO_NONE; m_nest_level++; break; case ')': m_filter->pop_expression(); m_nest_level--; break; case 'o': if(m_scanpos != 0 && m_state != ST_NEED_EXPRESSION) { if(next() == 'r') { m_last_boolop = BO_OR; } else { throw sinsp_exception("syntax error in filter at position " + to_string((long long)m_scanpos)); } if(m_state != ST_EXPRESSION_DONE) { throw sinsp_exception("unexpected 'or' after " + m_fltstr.substr(0, m_scanpos)); } m_state = ST_NEED_EXPRESSION; } else { parse_check(); m_state = ST_EXPRESSION_DONE; } break; case 'a': if(m_scanpos != 0 && m_state != ST_NEED_EXPRESSION) { if(next() == 'n' && next() == 'd') { m_last_boolop = BO_AND; } else { throw sinsp_exception("syntax error in filter at position " + to_string((long long)m_scanpos)); } if(m_state != ST_EXPRESSION_DONE) { throw sinsp_exception("unexpected 'and' after " + m_fltstr.substr(0, m_scanpos)); } m_state = ST_NEED_EXPRESSION; } else { parse_check(); m_state = ST_EXPRESSION_DONE; } break; case 'n': if(next() == 'o' && next() == 't') { m_last_boolop = (boolop)((uint32_t)m_last_boolop | BO_NOT); } else { throw sinsp_exception("syntax error in filter at position " + to_string((long long) m_scanpos)); } if(m_state != ST_EXPRESSION_DONE && m_state != ST_NEED_EXPRESSION) { throw sinsp_exception("unexpected 'not' after " + m_fltstr.substr(0, m_scanpos)); } m_state = ST_NEED_EXPRESSION; break; default: if(m_state == ST_NEED_EXPRESSION) { parse_check(); m_state = ST_EXPRESSION_DONE; } else { throw sinsp_exception("syntax error in filter at position " + to_string((long long) m_scanpos)); } break; } } vector components = sinsp_split(m_fltstr, ' '); return m_filter; } sinsp_evttype_filter::sinsp_evttype_filter() { } sinsp_evttype_filter::~sinsp_evttype_filter() { for(const auto &val : m_filters) { delete val.second->filter; delete val.second; } for(auto &ruleset : m_rulesets) { delete ruleset; } m_filters.clear(); } sinsp_evttype_filter::ruleset_filters::ruleset_filters() { memset(m_filter_by_evttype, 0, PPM_EVENT_MAX * sizeof(list *)); memset(m_filter_by_syscall, 0, PPM_SC_MAX * sizeof(list *)); } sinsp_evttype_filter::ruleset_filters::~ruleset_filters() { for(int i = 0; i < PPM_EVENT_MAX; i++) { if(m_filter_by_evttype[i]) { delete m_filter_by_evttype[i]; m_filter_by_evttype[i] = NULL; } } for(int i = 0; i < PPM_SC_MAX; i++) { if(m_filter_by_syscall[i]) { delete m_filter_by_syscall[i]; m_filter_by_syscall[i] = NULL; } } } void sinsp_evttype_filter::ruleset_filters::add_filter(filter_wrapper *wrap) { for(uint32_t etype = 0; etype < PPM_EVENT_MAX; etype++) { if(wrap->evttypes[etype]) { if(!m_filter_by_evttype[etype]) { m_filter_by_evttype[etype] = new std::list(); } m_filter_by_evttype[etype]->push_back(wrap); } } for(uint32_t syscall = 0; syscall < PPM_SC_MAX; syscall++) { if(wrap->syscalls[syscall]) { if(!m_filter_by_syscall[syscall]) { m_filter_by_syscall[syscall] = new std::list(); } m_filter_by_syscall[syscall]->push_back(wrap); } } } void sinsp_evttype_filter::ruleset_filters::remove_filter(filter_wrapper *wrap) { for(uint32_t etype = 0; etype < PPM_EVENT_MAX; etype++) { if(wrap->evttypes[etype]) { if(m_filter_by_evttype[etype]) { m_filter_by_evttype[etype]->erase(std::remove(m_filter_by_evttype[etype]->begin(), m_filter_by_evttype[etype]->end(), wrap), m_filter_by_evttype[etype]->end()); if(m_filter_by_evttype[etype]->size() == 0) { delete m_filter_by_evttype[etype]; m_filter_by_evttype[etype] = NULL; } } } } for(uint32_t syscall = 0; syscall < PPM_SC_MAX; syscall++) { if(wrap->syscalls[syscall]) { if(m_filter_by_syscall[syscall]) { m_filter_by_syscall[syscall]->erase(std::remove(m_filter_by_syscall[syscall]->begin(), m_filter_by_syscall[syscall]->end(), wrap), m_filter_by_syscall[syscall]->end()); if(m_filter_by_syscall[syscall]->size() == 0) { delete m_filter_by_syscall[syscall]; m_filter_by_syscall[syscall] = NULL; } } } } } bool sinsp_evttype_filter::ruleset_filters::run(sinsp_evt *evt) { list *filters; uint16_t etype = evt->m_pevt->type; if(etype == PPME_GENERIC_E || etype == PPME_GENERIC_X) { sinsp_evt_param *parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(uint16_t)); uint16_t evid = *(uint16_t *)parinfo->m_val; filters = m_filter_by_syscall[evid]; } else { filters = m_filter_by_evttype[etype]; } if (!filters) { return false; } for (auto &wrap : *filters) { if(wrap->filter->run(evt)) { return true; } } return false; } void sinsp_evttype_filter::ruleset_filters::evttypes_for_ruleset(std::vector &evttypes) { evttypes.assign(PPM_EVENT_MAX+1, false); for(uint32_t etype = 0; etype < PPM_EVENT_MAX; etype++) { list *filters = m_filter_by_evttype[etype]; if(filters) { evttypes[etype] = true; } } } void sinsp_evttype_filter::ruleset_filters::syscalls_for_ruleset(std::vector &syscalls) { syscalls.assign(PPM_SC_MAX+1, false); for(uint32_t evid = 0; evid < PPM_SC_MAX; evid++) { list *filters = m_filter_by_syscall[evid]; if(filters) { syscalls[evid] = true; } } } void sinsp_evttype_filter::add(string &name, set &evttypes, set &syscalls, set &tags, sinsp_filter *filter) { filter_wrapper *wrap = new filter_wrapper(); wrap->filter = filter; // If no evttypes or syscalls are specified, the filter is // enabled for all evttypes/syscalls. bool def = ((evttypes.size() == 0 && syscalls.size() == 0) ? true : false); wrap->evttypes.assign(PPM_EVENT_MAX+1, def); for(auto &evttype : evttypes) { wrap->evttypes[evttype] = true; } wrap->syscalls.assign(PPM_SC_MAX+1, def); for(auto &syscall : syscalls) { wrap->syscalls[syscall] = true; } m_filters.insert(pair(name, wrap)); for(const auto &tag: tags) { auto it = m_filter_by_tag.lower_bound(tag); if(it == m_filter_by_tag.end() || it->first != tag) { it = m_filter_by_tag.emplace_hint(it, std::make_pair(tag, std::list())); } it->second.push_back(wrap); } } void sinsp_evttype_filter::enable(const string &pattern, bool enabled, uint16_t ruleset) { regex re(pattern); while (m_rulesets.size() < (size_t) ruleset + 1) { m_rulesets.push_back(new ruleset_filters()); } for(const auto &val : m_filters) { if (regex_match(val.first, re)) { if(enabled) { m_rulesets[ruleset]->add_filter(val.second); } else { m_rulesets[ruleset]->remove_filter(val.second); } } } } void sinsp_evttype_filter::enable_tags(const set &tags, bool enabled, uint16_t ruleset) { while (m_rulesets.size() < (size_t) ruleset + 1) { m_rulesets.push_back(new ruleset_filters()); } for(const auto &tag : tags) { for(const auto &wrap : m_filter_by_tag[tag]) { if(enabled) { m_rulesets[ruleset]->add_filter(wrap); } else { m_rulesets[ruleset]->remove_filter(wrap); } } } } bool sinsp_evttype_filter::run(sinsp_evt *evt, uint16_t ruleset) { if(m_rulesets.size() < (size_t) ruleset + 1) { return false; } return m_rulesets[ruleset]->run(evt); } void sinsp_evttype_filter::evttypes_for_ruleset(std::vector &evttypes, uint16_t ruleset) { return m_rulesets[ruleset]->evttypes_for_ruleset(evttypes); } void sinsp_evttype_filter::syscalls_for_ruleset(std::vector &syscalls, uint16_t ruleset) { return m_rulesets[ruleset]->syscalls_for_ruleset(syscalls); } sinsp_filter_factory::sinsp_filter_factory(sinsp *inspector) : m_inspector(inspector) { } sinsp_filter_factory::~sinsp_filter_factory() { } gen_event_filter *sinsp_filter_factory::new_filter() { return new sinsp_filter(m_inspector); } gen_event_filter_check *sinsp_filter_factory::new_filtercheck(const char *fldname) { return g_filterlist.new_filter_check_from_fldname(fldname, m_inspector, true); } #endif // HAS_FILTERING sysdig-0.26.4/userspace/libsinsp/filter.h000066400000000000000000000132451352731327100203710ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #include #ifdef HAS_FILTERING #include "gen_filter.h" /** @defgroup filter Filtering events * Filtering infrastructure. * @{ */ /*! \brief This is the class that runs sysdig-type filters. */ class SINSP_PUBLIC sinsp_filter : public gen_event_filter { public: sinsp_filter(sinsp* inspector); ~sinsp_filter(); private: sinsp* m_inspector; friend class sinsp_evt_formatter; }; /*! \brief This is the class that compiles sysdig-type filters. */ class SINSP_PUBLIC sinsp_filter_compiler { public: /*! \brief Constructs the compiler. \param inspector Pointer to the inspector instance that will generate the events to be filtered. \param fltstr the filter string to compile. \param ttable_only for internal use only. \note Throws a sinsp_exception if the filter syntax is not valid. */ sinsp_filter_compiler(sinsp* inspector/* xxx needed? */, const string& fltstr, bool ttable_only=false); ~sinsp_filter_compiler(); sinsp_filter* compile(); private: enum state { ST_EXPRESSION_DONE, ST_NEED_EXPRESSION, }; sinsp_filter* compile_(); char next(); bool compare_no_consume(const string& str); vector next_operand(bool expecting_first_operand, bool in_clause); cmpop next_comparison_operator(); void parse_check(); static bool isblank(char c); static bool is_special_char(char c); static bool is_bracket(char c); sinsp* m_inspector; bool m_ttable_only; string m_fltstr; int32_t m_scanpos; int32_t m_scansize; state m_state; boolop m_last_boolop; int32_t m_nest_level; sinsp_filter* m_filter; friend class sinsp_evt_formatter; }; /*! \brief This class represents a filter optimized using event types. It actually consists of collections of sinsp_filter objects grouped by event type. */ class SINSP_PUBLIC sinsp_evttype_filter { public: sinsp_evttype_filter(); virtual ~sinsp_evttype_filter(); void add(std::string &name, std::set &evttypes, std::set &syscalls, std::set &tags, sinsp_filter* filter); // rulesets are arbitrary numbers and should be managed by the caller. // Note that rulesets are used to index into a std::vector so // specifying unnecessarily large rulesets will result in // unnecessarily large vectors. // Find those rules matching the provided pattern and set // their enabled status to enabled. void enable(const std::string &pattern, bool enabled, uint16_t ruleset = 0); // Find those rules that have a tag in the set of tags and set // their enabled status to enabled. Note that the enabled // status is on the rules, and not the tags--if a rule R has // tags (a, b), and you call enable_tags([a], true) and then // enable_tags([b], false), R will be disabled despite the // fact it has tag a and was enabled by the first call to // enable_tags. void enable_tags(const std::set &tags, bool enabled, uint16_t ruleset = 0); // Match all filters against the provided event. bool run(sinsp_evt *evt, uint16_t ruleset = 0); // Populate the provided vector, indexed by event type, of the // event types associated with the given ruleset id. For // example, evttypes[10] = true would mean that this ruleset // relates to event type 10. void evttypes_for_ruleset(std::vector &evttypes, uint16_t ruleset); // Populate the provided vector, indexed by syscall code, of the // syscall codes associated with the given ruleset id. For // example, syscalls[10] = true would mean that this ruleset // relates to syscall code 10. void syscalls_for_ruleset(std::vector &syscalls, uint16_t ruleset); private: struct filter_wrapper { sinsp_filter *filter; // Indexes from event type to enabled/disabled. std::vector evttypes; // Indexes from syscall code to enabled/disabled. std::vector syscalls; }; // A group of filters all having the same ruleset class ruleset_filters { public: ruleset_filters(); virtual ~ruleset_filters(); void add_filter(filter_wrapper *wrap); void remove_filter(filter_wrapper *wrap); bool run(sinsp_evt *evt); void evttypes_for_ruleset(std::vector &evttypes); void syscalls_for_ruleset(std::vector &syscalls); private: // Maps from event type to filter. There can be multiple // filters per event type. std::list *m_filter_by_evttype[PPM_EVENT_MAX]; // Maps from syscall number to filter. There can be multiple // filters per syscall number std::list *m_filter_by_syscall[PPM_SC_MAX]; }; std::vector m_rulesets; // Maps from tag to list of filters having that tag. std::map> m_filter_by_tag; // This holds all the filters passed to add(), so they can // be cleaned up. map m_filters; }; /*@}*/ class sinsp_filter_factory : public gen_event_filter_factory { public: sinsp_filter_factory(sinsp *inspector); virtual ~sinsp_filter_factory(); gen_event_filter *new_filter(); gen_event_filter_check *new_filtercheck(const char *fldname); protected: sinsp *m_inspector; }; #endif // HAS_FILTERING sysdig-0.26.4/userspace/libsinsp/filter_value.h000066400000000000000000000031621352731327100215620ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #include // Used for CO_IN/CO_PMATCH filterchecks using PT_CHARBUFs to allow // for quick multi-value comparisons. Should also work for any // filtercheck with a buffer and length. When compiling with gnu // compilers, use the built in but not standard _hash_impl::hash // function, which uses murmurhash2 and is quite fast. Otherwise, uses // http://www.cse.yorku.ca/~oz/hash.html. typedef std::pair filter_value_t; struct g_hash_membuf { size_t operator()(filter_value_t val) const { #if defined(__GNUC__) && !defined(__clang__) return std::_Hash_impl::hash(val.first, val.second); #else size_t hash = 5381; for(uint8_t *p = val.first; (uint32_t)(p-val.first) < val.second; p++) { int c = *p; hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ } return hash; #endif } }; struct g_equal_to_membuf { bool operator()(filter_value_t a, filter_value_t b) const { return (a.second == b.second && memcmp(a.first, b.first, a.second) == 0); } }; sysdig-0.26.4/userspace/libsinsp/filterchecks.cpp000066400000000000000000006176121352731327100221150ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #ifndef _WIN32 #include #endif #include "sinsp.h" #include "sinsp_int.h" #include "dns_manager.h" #ifdef HAS_FILTERING #include "filter.h" #include "filterchecks.h" #include "protodecoder.h" #include "tracers.h" #include "value_parser.h" extern sinsp_evttables g_infotables; int32_t g_csysdig_screen_w = -1; bool g_filterchecks_force_raw_times = false; #define RETURN_EXTRACT_VAR(x) do { \ *len = sizeof((x)); \ return (uint8_t*) &(x); \ } while(0) #define RETURN_EXTRACT_PTR(x) do { \ *len = sizeof(*(x)); \ return (uint8_t*) (x); \ } while(0) #define RETURN_EXTRACT_STRING(x) do { \ *len = (x).size(); \ return (uint8_t*) (x).c_str(); \ } while(0) #define RETURN_EXTRACT_CSTR(x) do { \ if((x)) \ { \ *len = strlen((char *) ((x))); \ } \ return (uint8_t*) ((x)); \ } while(0) /////////////////////////////////////////////////////////////////////////////// // Helper functions /////////////////////////////////////////////////////////////////////////////// int32_t gmt2local(time_t t) { int dt, dir; struct tm *gmt, *loc; struct tm sgmt; if(t == 0) { t = time(NULL); } gmt = &sgmt; *gmt = *gmtime(&t); loc = localtime(&t); dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 + (loc->tm_min - gmt->tm_min) * 60; dir = loc->tm_year - gmt->tm_year; if(dir == 0) { dir = loc->tm_yday - gmt->tm_yday; } dt += dir * 24 * 60 * 60; return dt; } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_fd implementation /////////////////////////////////////////////////////////////////////////////// const filtercheck_field_info sinsp_filter_check_fd_fields[] = { {PT_INT64, EPF_NONE, PF_ID, "fd.num", "the unique number identifying the file descriptor."}, {PT_CHARBUF, EPF_NONE, PF_DEC, "fd.type", "type of FD. Can be 'file', 'directory', 'ipv4', 'ipv6', 'unix', 'pipe', 'event', 'signalfd', 'eventpoll', 'inotify' or 'signalfd'."}, {PT_CHARBUF, EPF_NONE, PF_DEC, "fd.typechar", "type of FD as a single character. Can be 'f' for file, 4 for IPv4 socket, 6 for IPv6 socket, 'u' for unix socket, p for pipe, 'e' for eventfd, 's' for signalfd, 'l' for eventpoll, 'i' for inotify, 'o' for unknown."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.name", "FD full name. If the fd is a file, this field contains the full path. If the FD is a socket, this field contain the connection tuple."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.directory", "If the fd is a file, the directory that contains it."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.filename", "If the fd is a file, the filename without the path."}, {PT_IPADDR, EPF_FILTER_ONLY, PF_NA, "fd.ip", "matches the ip address (client or server) of the fd."}, {PT_IPADDR, EPF_NONE, PF_NA, "fd.cip", "client IP address."}, {PT_IPADDR, EPF_NONE, PF_NA, "fd.sip", "server IP address."}, {PT_IPADDR, EPF_NONE, PF_NA, "fd.lip", "local IP address."}, {PT_IPADDR, EPF_NONE, PF_NA, "fd.rip", "remote IP address."}, {PT_PORT, EPF_FILTER_ONLY, PF_DEC, "fd.port", "matches the port (either client or server) of the fd."}, {PT_PORT, EPF_NONE, PF_DEC, "fd.cport", "for TCP/UDP FDs, the client port."}, {PT_PORT, EPF_NONE, PF_DEC, "fd.sport", "for TCP/UDP FDs, server port."}, {PT_PORT, EPF_NONE, PF_DEC, "fd.lport", "for TCP/UDP FDs, the local port."}, {PT_PORT, EPF_NONE, PF_DEC, "fd.rport", "for TCP/UDP FDs, the remote port."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.l4proto", "the IP protocol of a socket. Can be 'tcp', 'udp', 'icmp' or 'raw'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.sockfamily", "the socket family for socket events. Can be 'ip' or 'unix'."}, {PT_BOOL, EPF_NONE, PF_NA, "fd.is_server", "'true' if the process owning this FD is the server endpoint in the connection."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.uid", "a unique identifier for the FD, created by chaining the FD number and the thread ID."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.containername", "chaining of the container ID and the FD name. Useful when trying to identify which container an FD belongs to."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.containerdirectory", "chaining of the container ID and the directory name. Useful when trying to identify which container a directory belongs to."}, {PT_PORT, EPF_FILTER_ONLY, PF_NA, "fd.proto", "matches the protocol (either client or server) of the fd."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.cproto", "for TCP/UDP FDs, the client protocol."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.sproto", "for TCP/UDP FDs, server protocol."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.lproto", "for TCP/UDP FDs, the local protocol."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.rproto", "for TCP/UDP FDs, the remote protocol."}, {PT_IPNET, EPF_FILTER_ONLY, PF_NA, "fd.net", "matches the IP network (client or server) of the fd."}, {PT_IPNET, EPF_FILTER_ONLY, PF_NA, "fd.cnet", "matches the client IP network of the fd."}, {PT_IPNET, EPF_FILTER_ONLY, PF_NA, "fd.snet", "matches the server IP network of the fd."}, {PT_IPNET, EPF_FILTER_ONLY, PF_NA, "fd.lnet", "matches the local IP network of the fd."}, {PT_IPNET, EPF_FILTER_ONLY, PF_NA, "fd.rnet", "matches the remote IP network of the fd."}, {PT_BOOL, EPF_NONE, PF_NA, "fd.connected", "for TCP/UDP FDs, 'true' if the socket is connected."}, {PT_BOOL, EPF_NONE, PF_NA, "fd.name_changed", "True when an event changes the name of an fd used by this event. This can occur in some cases such as udp connections where the connection tuple changes."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.cip.name", "Domain name associated with the client IP address."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.sip.name", "Domain name associated with the server IP address."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.lip.name", "Domain name associated with the local IP address."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fd.rip.name", "Domain name associated with the remote IP address."}, {PT_INT32, EPF_NONE, PF_HEX, "fd.dev", "device number (major/minor) containing the referenced file"}, {PT_INT32, EPF_NONE, PF_DEC, "fd.dev.major", "major device number containing the referenced file"}, {PT_INT32, EPF_NONE, PF_DEC, "fd.dev.minor", "minor device number containing the referenced file"}, }; sinsp_filter_check_fd::sinsp_filter_check_fd() { m_tinfo = NULL; m_fdinfo = NULL; m_info.m_name = "fd"; m_info.m_fields = sinsp_filter_check_fd_fields; m_info.m_nfields = sizeof(sinsp_filter_check_fd_fields) / sizeof(sinsp_filter_check_fd_fields[0]); m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; } sinsp_filter_check* sinsp_filter_check_fd::allocate_new() { return (sinsp_filter_check*) new sinsp_filter_check_fd(); } bool sinsp_filter_check_fd::extract_fdname_from_creator(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { const char* resolved_argstr; uint16_t etype = evt->get_type(); if(PPME_IS_ENTER(etype)) { return false; } switch(etype) { case PPME_SYSCALL_OPEN_X: case PPME_SOCKET_ACCEPT_X: case PPME_SOCKET_ACCEPT_5_X: case PPME_SOCKET_ACCEPT4_X: case PPME_SOCKET_ACCEPT4_5_X: case PPME_SYSCALL_CREAT_X: { const char* argstr = evt->get_param_as_str(1, &resolved_argstr, m_inspector->get_buffer_format()); if(resolved_argstr[0] != 0) { m_tstr = resolved_argstr; } else { m_tstr = argstr; } return true; } case PPME_SOCKET_CONNECT_X: { const char* argstr = evt->get_param_as_str(1, &resolved_argstr, m_inspector->get_buffer_format()); if(resolved_argstr[0] != 0) { m_tstr = resolved_argstr; } else { m_tstr = argstr; } return true; } case PPME_SYSCALL_OPENAT_X: case PPME_SYSCALL_OPENAT_2_X: { sinsp_evt enter_evt; sinsp_evt_param *parinfo; char *name; uint32_t namelen; string sdir; if(etype == PPME_SYSCALL_OPENAT_X) { // // XXX This is highly inefficient, as it re-requests the enter event and then // does unnecessary allocations and copies. We assume that failed openat() happen // rarely enough that we don't care. // if(!m_inspector->get_parser()->retrieve_enter_event(&enter_evt, evt)) { return false; } } parinfo = etype == PPME_SYSCALL_OPENAT_X ? enter_evt.get_param(1) : evt->get_param(2); name = parinfo->m_val; namelen = parinfo->m_len; parinfo = etype == PPME_SYSCALL_OPENAT_X ? enter_evt.get_param(0) : evt->get_param(1); ASSERT(parinfo->m_len == sizeof(int64_t)); int64_t dirfd = *(int64_t *)parinfo->m_val; sinsp_parser::parse_openat_dir(evt, name, dirfd, &sdir); char fullpath[SCAP_MAX_PATH_SIZE]; sinsp_utils::concatenate_paths(fullpath, SCAP_MAX_PATH_SIZE, sdir.c_str(), (uint32_t)sdir.length(), name, namelen); m_tstr = fullpath; if(sanitize_strings) { sanitize_string(m_tstr); } return true; } default: m_tstr = ""; return true; } } uint8_t* sinsp_filter_check_fd::extract_from_null_fd(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = 0; // // Even is there's no fd, we still try to extract a name from exit events that create // one. With these events, the fact that there's no FD means that the call failed, // but even if that happened we still want to collect the name. // switch(m_field_id) { case TYPE_FDNAME: { if(extract_fdname_from_creator(evt, len, sanitize_strings) == true) { RETURN_EXTRACT_STRING(m_tstr); } else { return NULL; } } case TYPE_CONTAINERNAME: { if(extract_fdname_from_creator(evt, len, sanitize_strings) == true) { m_tstr = m_tinfo->m_container_id + ':' + m_tstr; RETURN_EXTRACT_STRING(m_tstr); } else { return NULL; } } case TYPE_DIRECTORY: case TYPE_CONTAINERDIRECTORY: { if(extract_fdname_from_creator(evt, len, sanitize_strings) == true) { if(sanitize_strings) { sanitize_string(m_tstr); } size_t pos = m_tstr.rfind('/'); if(pos != string::npos && pos != 0) { if(pos < m_tstr.size() - 1) { m_tstr.resize(pos); } } else { m_tstr = "/"; } if(m_field_id == TYPE_CONTAINERDIRECTORY) { m_tstr = m_tinfo->m_container_id + ':' + m_tstr; } RETURN_EXTRACT_STRING(m_tstr); } else { return NULL; } } case TYPE_FILENAME: { if(evt->get_type() != PPME_SYSCALL_OPEN_E && evt->get_type() != PPME_SYSCALL_OPENAT_E && evt->get_type() != PPME_SYSCALL_OPENAT_2_E && evt->get_type() != PPME_SYSCALL_CREAT_E) { return NULL; } if(extract_fdname_from_creator(evt, len, sanitize_strings) == true) { if(sanitize_strings) { sanitize_string(m_tstr); } size_t pos = m_tstr.rfind('/'); if(pos != string::npos) { if(pos < m_tstr.size() - 1) { m_tstr = m_tstr.substr(pos + 1, string::npos); } } RETURN_EXTRACT_STRING(m_tstr); } else { return NULL; } } case TYPE_FDTYPECHAR: *len = 1; switch(PPME_MAKE_ENTER(evt->get_type())) { case PPME_SYSCALL_OPEN_E: case PPME_SYSCALL_OPENAT_E: case PPME_SYSCALL_OPENAT_2_E: case PPME_SYSCALL_CREAT_E: m_tcstr[0] = CHAR_FD_FILE; m_tcstr[1] = 0; return m_tcstr; case PPME_SOCKET_SOCKET_E: case PPME_SOCKET_ACCEPT_E: case PPME_SOCKET_ACCEPT_5_E: case PPME_SOCKET_ACCEPT4_E: case PPME_SOCKET_ACCEPT4_5_E: // // Note, this is not accurate, because it always // returns IPv4 even if this could be IPv6 or unix. // For the moment, I assume it's better than nothing, and doing // real event parsing here would be a pain. // m_tcstr[0] = CHAR_FD_IPV4_SOCK; m_tcstr[1] = 0; return m_tcstr; case PPME_SYSCALL_PIPE_E: m_tcstr[0] = CHAR_FD_FIFO; m_tcstr[1] = 0; return m_tcstr; case PPME_SYSCALL_EVENTFD_E: m_tcstr[0] = CHAR_FD_EVENT; m_tcstr[1] = 0; return m_tcstr; case PPME_SYSCALL_SIGNALFD_E: m_tcstr[0] = CHAR_FD_SIGNAL; m_tcstr[1] = 0; return m_tcstr; case PPME_SYSCALL_TIMERFD_CREATE_E: m_tcstr[0] = CHAR_FD_TIMERFD; m_tcstr[1] = 0; return m_tcstr; case PPME_SYSCALL_INOTIFY_INIT_E: m_tcstr[0] = CHAR_FD_INOTIFY; m_tcstr[1] = 0; return m_tcstr; default: m_tcstr[0] = 'o'; m_tcstr[1] = 0; return m_tcstr; } default: return NULL; } } uint8_t* sinsp_filter_check_fd::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = 0; ASSERT(evt); if(!extract_fd(evt)) { return NULL; } // // TYPE_FDNUM doesn't need fdinfo // if(m_field_id == TYPE_FDNUM) { RETURN_EXTRACT_VAR(m_tinfo->m_lastevent_fd); } switch(m_field_id) { case TYPE_FDNAME: case TYPE_CONTAINERNAME: if(m_fdinfo == NULL) { return extract_from_null_fd(evt, len, sanitize_strings); } if(evt->get_type() == PPME_SOCKET_CONNECT_X) { sinsp_evt_param *parinfo; parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(uint64_t)); int64_t retval = *(int64_t*)parinfo->m_val; if(retval < 0) { return extract_from_null_fd(evt, len, sanitize_strings); } } if(m_field_id == TYPE_CONTAINERNAME) { ASSERT(m_tinfo != NULL); m_tstr = m_tinfo->m_container_id + ':' + m_fdinfo->m_name; } else { m_tstr = m_fdinfo->m_name; } if(sanitize_strings) { sanitize_string(m_tstr); } RETURN_EXTRACT_STRING(m_tstr); case TYPE_FDTYPE: if(m_fdinfo == NULL) { return NULL; } else { uint8_t *typestr = (uint8_t*)m_fdinfo->get_typestring(); RETURN_EXTRACT_CSTR(typestr); } case TYPE_DIRECTORY: case TYPE_CONTAINERDIRECTORY: { if(m_fdinfo == NULL) { return extract_from_null_fd(evt, len, sanitize_strings); } if(!(m_fdinfo->is_file() || m_fdinfo->is_directory())) { return NULL; } m_tstr = m_fdinfo->m_name; if(sanitize_strings) { sanitize_string(m_tstr); } if(m_fdinfo->is_file()) { size_t pos = m_tstr.rfind('/'); if(pos != string::npos && pos != 0) { if(pos < m_tstr.size() - 1) { m_tstr.resize(pos); } } else { m_tstr = "/"; } } if(m_field_id == TYPE_CONTAINERDIRECTORY) { m_tstr = m_tinfo->m_container_id + ':' + m_tstr; } RETURN_EXTRACT_STRING(m_tstr); } case TYPE_FILENAME: { if(m_fdinfo == NULL) { return extract_from_null_fd(evt, len, sanitize_strings); } if(!m_fdinfo->is_file()) { return NULL; } m_tstr = m_fdinfo->m_name; if(sanitize_strings) { sanitize_string(m_tstr); } size_t pos = m_tstr.rfind('/'); if(pos != string::npos) { if(pos < m_tstr.size() - 1) { m_tstr = m_tstr.substr(pos + 1, string::npos); } } else { m_tstr = "/"; } RETURN_EXTRACT_STRING(m_tstr); } case TYPE_FDTYPECHAR: if(m_fdinfo == NULL) { return extract_from_null_fd(evt, len, sanitize_strings); } *len = 1; m_tcstr[0] = m_fdinfo->get_typechar(); m_tcstr[1] = 0; return m_tcstr; case TYPE_CNET: case TYPE_CLIENTIP: { if(m_fdinfo == NULL) { return NULL; } if(m_fdinfo->is_role_none()) { return NULL; } scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type == SCAP_FD_IPV4_SOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip); } else if (evt_type == SCAP_FD_IPV6_SOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip); } } break; case TYPE_CLIENTIP_NAME: { if(m_fdinfo == NULL) { return NULL; } if(m_fdinfo->is_role_none()) { return NULL; } m_tstr.clear(); scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type == SCAP_FD_IPV4_SOCK) { m_tstr = sinsp_dns_manager::get().name_of(AF_INET, &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip, evt->get_ts()); } else if (evt_type == SCAP_FD_IPV6_SOCK) { m_tstr = sinsp_dns_manager::get().name_of(AF_INET6, &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip.m_b[0], evt->get_ts()); } if(!m_tstr.empty()) { RETURN_EXTRACT_STRING(m_tstr); } } break; case TYPE_SNET: case TYPE_SERVERIP: { if(m_fdinfo == NULL) { return NULL; } if(m_fdinfo->is_role_none()) { return NULL; } scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type == SCAP_FD_IPV4_SOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip); } else if(evt_type == SCAP_FD_IPV4_SERVSOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_ip); } else if(evt_type == SCAP_FD_IPV6_SOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip); } else if(evt_type == SCAP_FD_IPV6_SERVSOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv6serverinfo.m_ip); } } break; case TYPE_SERVERIP_NAME: { if(m_fdinfo == NULL) { return NULL; } if(m_fdinfo->is_role_none()) { return NULL; } m_tstr.clear(); scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type == SCAP_FD_IPV4_SOCK) { m_tstr = sinsp_dns_manager::get().name_of(AF_INET, &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip, evt->get_ts()); } else if(evt_type == SCAP_FD_IPV4_SERVSOCK) { m_tstr = sinsp_dns_manager::get().name_of(AF_INET, &m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_ip, evt->get_ts()); } else if (evt_type == SCAP_FD_IPV6_SOCK) { m_tstr = sinsp_dns_manager::get().name_of(AF_INET6, &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip.m_b[0], evt->get_ts()); } else if(evt_type == SCAP_FD_IPV6_SERVSOCK) { m_tstr = sinsp_dns_manager::get().name_of(AF_INET6, &m_fdinfo->m_sockinfo.m_ipv6serverinfo.m_ip.m_b[0], evt->get_ts()); } if(!m_tstr.empty()) { RETURN_EXTRACT_STRING(m_tstr); } } break; case TYPE_LNET: case TYPE_RNET: case TYPE_LIP: case TYPE_RIP: case TYPE_LIP_NAME: case TYPE_RIP_NAME: { if(m_fdinfo == NULL) { return NULL; } scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type != SCAP_FD_IPV4_SOCK && evt_type != SCAP_FD_IPV6_SOCK) { return NULL; } if(m_fdinfo->is_role_none()) { return NULL; } bool is_local; if(evt_type == SCAP_FD_IPV4_SOCK) { is_local = m_inspector->get_ifaddr_list()->is_ipv4addr_in_local_machine(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip, m_tinfo); } else { is_local = m_inspector->get_ifaddr_list()->is_ipv6addr_in_local_machine(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip, m_tinfo); } if(m_field_id != TYPE_LIP_NAME && m_field_id != TYPE_RIP_NAME) { if(is_local) { if(m_field_id == TYPE_LIP || m_field_id == TYPE_LNET) { if(evt_type == SCAP_FD_IPV4_SOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip); } else { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip); } } else { if(evt_type == SCAP_FD_IPV4_SOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip); } else { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip); } } } else { if(m_field_id == TYPE_LIP || m_field_id == TYPE_LNET) { if(evt_type == SCAP_FD_IPV4_SOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip); } else { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip); } } else { if(evt_type == SCAP_FD_IPV4_SOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip); } else { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip); } } } } else { m_tstr.clear(); if(is_local) { if(m_field_id == TYPE_LIP_NAME) { if(evt_type == SCAP_FD_IPV4_SOCK) { m_tstr = sinsp_dns_manager::get().name_of(AF_INET, &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip, evt->get_ts()); } else { m_tstr = sinsp_dns_manager::get().name_of(AF_INET6, &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip.m_b[0], evt->get_ts()); } } else { if(evt_type == SCAP_FD_IPV4_SOCK) { m_tstr = sinsp_dns_manager::get().name_of(AF_INET, &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip, evt->get_ts()); } else { m_tstr = sinsp_dns_manager::get().name_of(AF_INET6, &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip.m_b[0], evt->get_ts()); } } } else { if(m_field_id == TYPE_LIP_NAME) { if(evt_type == SCAP_FD_IPV4_SOCK) { m_tstr = sinsp_dns_manager::get().name_of(AF_INET, &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip, evt->get_ts()); } else { m_tstr = sinsp_dns_manager::get().name_of(AF_INET6, &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip.m_b[0], evt->get_ts()); } } else { if(evt_type == SCAP_FD_IPV4_SOCK) { m_tstr = sinsp_dns_manager::get().name_of(AF_INET, &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip, evt->get_ts()); } else { m_tstr = sinsp_dns_manager::get().name_of(AF_INET6, &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip.m_b[0], evt->get_ts()); } } } if(!m_tstr.empty()) { RETURN_EXTRACT_STRING(m_tstr); } } } break; case TYPE_CLIENTPORT: { if(m_fdinfo == NULL) { return NULL; } scap_fd_type evt_type = m_fdinfo->m_type; if(m_fdinfo->is_role_none()) { return NULL; } if(evt_type == SCAP_FD_IPV4_SOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport); } else if(evt_type == SCAP_FD_IPV6_SOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sport); } } case TYPE_CLIENTPROTO: { if(m_fdinfo == NULL) { return NULL; } scap_fd_type evt_type = m_fdinfo->m_type; if(m_fdinfo->is_role_none()) { return NULL; } m_tstr = ""; if(evt_type == SCAP_FD_IPV4_SOCK) { m_tstr = port_to_string(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport, this->m_fdinfo->get_l4proto(), m_inspector->m_hostname_and_port_resolution_enabled); } else if(evt_type == SCAP_FD_IPV6_SOCK) { m_tstr = port_to_string(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sport, this->m_fdinfo->get_l4proto(), m_inspector->m_hostname_and_port_resolution_enabled); } RETURN_EXTRACT_STRING(m_tstr); } case TYPE_SERVERPORT: { if(m_fdinfo == NULL) { return NULL; } scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type == SCAP_FD_IPV4_SOCK) { if(m_fdinfo->is_role_none()) { return NULL; } RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport); } else if(evt_type == SCAP_FD_IPV4_SERVSOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_port); } else if(evt_type == SCAP_FD_IPV6_SOCK) { if(m_fdinfo->is_role_none()) { return NULL; } RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport); } else if(evt_type == SCAP_FD_IPV6_SERVSOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv6serverinfo.m_port); } else { return NULL; } } case TYPE_SERVERPROTO: { if(m_fdinfo == NULL) { return NULL; } uint16_t nport = 0; scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type == SCAP_FD_IPV4_SOCK) { if(m_fdinfo->is_role_none()) { return NULL; } nport = m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport; } else if(evt_type == SCAP_FD_IPV4_SERVSOCK) { nport = m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_port; } else if(evt_type == SCAP_FD_IPV6_SOCK) { if(m_fdinfo->is_role_none()) { return NULL; } nport = m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport; } else if(evt_type == SCAP_FD_IPV6_SERVSOCK) { nport = m_fdinfo->m_sockinfo.m_ipv6serverinfo.m_port; } else { return NULL; } m_tstr = ""; if(evt_type == SCAP_FD_IPV4_SOCK) { m_tstr = port_to_string(nport, this->m_fdinfo->get_l4proto(), m_inspector->m_hostname_and_port_resolution_enabled); } else if(evt_type == SCAP_FD_IPV6_SOCK) { m_tstr = port_to_string(nport, this->m_fdinfo->get_l4proto(), m_inspector->m_hostname_and_port_resolution_enabled); } RETURN_EXTRACT_STRING(m_tstr); } case TYPE_LPORT: case TYPE_RPORT: { if(m_fdinfo == NULL) { return NULL; } scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type != SCAP_FD_IPV4_SOCK && evt_type != SCAP_FD_IPV6_SOCK) { return NULL; } if(m_fdinfo->is_role_none()) { return NULL; } bool is_local; if(evt_type == SCAP_FD_IPV4_SOCK) { is_local = m_inspector->get_ifaddr_list()->is_ipv4addr_in_local_machine(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip, m_tinfo); } else { is_local = m_inspector->get_ifaddr_list()->is_ipv6addr_in_local_machine(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip, m_tinfo); } if(is_local) { if(m_field_id == TYPE_LPORT || m_field_id == TYPE_LPROTO) { if(evt_type == SCAP_FD_IPV4_SOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport); } else { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sport); } } else { if(evt_type == SCAP_FD_IPV4_SOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport); } else { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport); } } } else { if(m_field_id == TYPE_LPORT || m_field_id == TYPE_LPROTO) { if(evt_type == SCAP_FD_IPV4_SOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport); } else { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport); } } else { if(evt_type == SCAP_FD_IPV4_SOCK) { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport); } else { RETURN_EXTRACT_VAR(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sport); } } } } case TYPE_LPROTO: case TYPE_RPROTO: { if(m_fdinfo == NULL) { return NULL; } scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type != SCAP_FD_IPV4_SOCK && evt_type != SCAP_FD_IPV6_SOCK) { return NULL; } if(m_fdinfo->is_role_none()) { return NULL; } int16_t nport = 0; bool is_local; if(evt_type == SCAP_FD_IPV4_SOCK) { is_local = m_inspector->get_ifaddr_list()->is_ipv4addr_in_local_machine(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip, m_tinfo); } else { is_local = m_inspector->get_ifaddr_list()->is_ipv6addr_in_local_machine(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip, m_tinfo); } if(is_local) { if(m_field_id == TYPE_LPORT || m_field_id == TYPE_LPROTO) { if(evt_type == SCAP_FD_IPV4_SOCK) { nport = m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport; } else { nport = m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sport; } } else { if(evt_type == SCAP_FD_IPV4_SOCK) { nport = m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport; } else { nport = m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport; } } } else { if(m_field_id == TYPE_LPORT || m_field_id == TYPE_LPROTO) { if(evt_type == SCAP_FD_IPV4_SOCK) { nport = m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport; } else { nport = m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport; } } else { if(evt_type == SCAP_FD_IPV4_SOCK) { nport = m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport; } else { nport = m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sport; } } } m_tstr = port_to_string(nport, this->m_fdinfo->get_l4proto(), m_inspector->m_hostname_and_port_resolution_enabled); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_L4PROTO: { if(m_fdinfo == NULL) { return NULL; } scap_l4_proto l4p = m_fdinfo->get_l4proto(); switch(l4p) { case SCAP_L4_TCP: m_tstr = "tcp"; break; case SCAP_L4_UDP: m_tstr = "udp"; break; case SCAP_L4_ICMP: m_tstr = "icmp"; break; case SCAP_L4_RAW: m_tstr = "raw"; break; default: m_tstr = ""; break; } RETURN_EXTRACT_STRING(m_tstr); } case TYPE_IS_SERVER: { if(m_fdinfo == NULL) { return NULL; } if(m_fdinfo->m_type == SCAP_FD_IPV4_SERVSOCK || m_fdinfo->m_type == SCAP_FD_IPV6_SERVSOCK) { m_tbool = true; } else if(m_fdinfo->m_type == SCAP_FD_IPV4_SOCK) { m_tbool = m_inspector->get_ifaddr_list()->is_ipv4addr_in_local_machine(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip, m_tinfo); } else if(m_fdinfo->m_type == SCAP_FD_IPV6_SOCK) { m_tbool = m_inspector->get_ifaddr_list()->is_ipv6addr_in_local_machine(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip, m_tinfo); } else { m_tbool = false; } RETURN_EXTRACT_VAR(m_tbool); } break; case TYPE_SOCKFAMILY: { if(m_fdinfo == NULL) { return NULL; } if(m_fdinfo->m_type == SCAP_FD_IPV4_SOCK || m_fdinfo->m_type == SCAP_FD_IPV6_SOCK || m_fdinfo->m_type == SCAP_FD_IPV4_SERVSOCK || m_fdinfo->m_type == SCAP_FD_IPV6_SERVSOCK) { m_tstr = "ip"; RETURN_EXTRACT_STRING(m_tstr); } else if(m_fdinfo->m_type == SCAP_FD_UNIX_SOCK) { m_tstr = "unix"; RETURN_EXTRACT_STRING(m_tstr); } else { return NULL; } } break; case TYPE_UID: { if(evt->get_type() == PPME_CONTAINER_JSON_E) { return NULL; } ASSERT(m_tinfo != NULL); m_tstr = to_string(m_tinfo->m_tid) + to_string(m_tinfo->m_lastevent_fd); RETURN_EXTRACT_STRING(m_tstr); } break; case TYPE_IS_CONNECTED: { if(m_fdinfo == NULL) { return NULL; } m_tbool = m_fdinfo->is_socket_connected(); RETURN_EXTRACT_VAR(m_tbool); } break; case TYPE_NAME_CHANGED: { if(m_fdinfo == NULL) { return NULL; } m_tbool = evt->fdinfo_name_changed(); RETURN_EXTRACT_VAR(m_tbool); } break; case TYPE_DEV: { if(m_fdinfo == NULL) { return NULL; } m_tbool = m_fdinfo->get_device(); RETURN_EXTRACT_VAR(m_tbool); } break; case TYPE_DEV_MAJOR: { if(m_fdinfo == NULL) { return NULL; } m_tbool = m_fdinfo->get_device_major(); RETURN_EXTRACT_VAR(m_tbool); } break; case TYPE_DEV_MINOR: { if(m_fdinfo == NULL) { return NULL; } m_tbool = m_fdinfo->get_device_minor(); RETURN_EXTRACT_VAR(m_tbool); } break; default: ASSERT(false); } return NULL; } bool sinsp_filter_check_fd::compare_ip(sinsp_evt *evt) { if(!extract_fd(evt)) { return false; } if(m_fdinfo != NULL) { scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type == SCAP_FD_IPV4_SOCK) { if(m_cmpop == CO_EQ || m_cmpop == CO_IN) { if(flt_compare(m_cmpop, PT_IPV4ADDR, &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip) || flt_compare(m_cmpop, PT_IPV4ADDR, &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip)) { return true; } } else if(m_cmpop == CO_NE) { if(flt_compare(m_cmpop, PT_IPV4ADDR, &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip) && flt_compare(m_cmpop, PT_IPV4ADDR, &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip)) { return true; } } else { throw sinsp_exception("filter error: IP filter only supports '=' and '!=' operators"); } } else if(evt_type == SCAP_FD_IPV4_SERVSOCK) { if(m_cmpop == CO_EQ || m_cmpop == CO_NE || m_cmpop == CO_IN) { return flt_compare(m_cmpop, PT_IPV4ADDR, &m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_ip); } else { throw sinsp_exception("filter error: IP filter only supports '=' and '!=' operators"); } } else if(evt_type == SCAP_FD_IPV6_SOCK) { if(m_cmpop == CO_EQ || m_cmpop == CO_IN) { if(flt_compare(m_cmpop, PT_IPV6ADDR, &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip) || flt_compare(m_cmpop, PT_IPV6ADDR, &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip)) { return true; } } else if(m_cmpop == CO_NE) { if(flt_compare(m_cmpop, PT_IPV6ADDR, &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip) && flt_compare(m_cmpop, PT_IPV6ADDR, &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip)) { return true; } } else { throw sinsp_exception("filter error: IP filter only supports '=' and '!=' operators"); } } else if(evt_type == SCAP_FD_IPV6_SERVSOCK) { if(m_cmpop == CO_EQ || m_cmpop == CO_NE || m_cmpop == CO_IN) { return flt_compare(m_cmpop, PT_IPV6ADDR, &m_fdinfo->m_sockinfo.m_ipv6serverinfo.m_ip); } else { throw sinsp_exception("filter error: IP filter only supports '=' and '!=' operators"); } } } return false; } bool sinsp_filter_check_fd::compare_net(sinsp_evt *evt) { if(!extract_fd(evt)) { return false; } if(m_fdinfo != NULL) { scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type == SCAP_FD_IPV4_SOCK) { if(m_cmpop == CO_EQ || m_cmpop == CO_IN) { if(flt_compare_ipv4net(m_cmpop, m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip, (ipv4net*)filter_value_p()) || flt_compare_ipv4net(m_cmpop, m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip, (ipv4net*)filter_value_p())) { return true; } } else if(m_cmpop == CO_NE) { if(flt_compare_ipv4net(m_cmpop, m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip, (ipv4net*)filter_value_p()) && flt_compare_ipv4net(m_cmpop, m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip, (ipv4net*)filter_value_p())) { return true; } } else { throw sinsp_exception("filter error: IP filter only supports '=' and '!=' operators"); } } else if(evt_type == SCAP_FD_IPV4_SERVSOCK) { if(flt_compare_ipv4net(m_cmpop, m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_ip, (ipv4net*)filter_value_p())) { return true; } } else if(evt_type == SCAP_FD_IPV6_SOCK) { if(m_cmpop == CO_EQ || m_cmpop == CO_IN) { if(flt_compare_ipv6net(m_cmpop, &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip, (ipv6addr*)filter_value_p()) || flt_compare_ipv6net(m_cmpop, &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip, (ipv6addr*)filter_value_p())) { return true; } } else if(m_cmpop == CO_NE) { if(flt_compare_ipv6net(m_cmpop, &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip, (ipv6addr*)filter_value_p()) && flt_compare_ipv6net(m_cmpop, &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip, (ipv6addr*)filter_value_p())) { return true; } } else { throw sinsp_exception("filter error: IP filter only supports '=' and '!=' operators"); } } else if(evt_type == SCAP_FD_IPV6_SERVSOCK) { if(flt_compare_ipv6net(m_cmpop, &m_fdinfo->m_sockinfo.m_ipv6serverinfo.m_ip, (ipv6addr*)filter_value_p())) { return true; } } } return false; } bool sinsp_filter_check_fd::compare_port(sinsp_evt *evt) { if(!extract_fd(evt)) { return false; } if(m_fdinfo != NULL) { uint16_t* sport; uint16_t* dport; scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type == SCAP_FD_IPV4_SOCK) { sport = &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport; dport = &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport; } else if(evt_type == SCAP_FD_IPV4_SERVSOCK) { sport = &m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_port; dport = &m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_port; } else if(evt_type == SCAP_FD_IPV6_SOCK) { sport = &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sport; dport = &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport; } else if(evt_type == SCAP_FD_IPV6_SERVSOCK) { sport = &m_fdinfo->m_sockinfo.m_ipv6serverinfo.m_port; dport = &m_fdinfo->m_sockinfo.m_ipv6serverinfo.m_port; } else { return false; } switch(m_cmpop) { case CO_EQ: if(*sport == *(uint16_t*)filter_value_p() || *dport == *(uint16_t*)filter_value_p()) { return true; } break; case CO_NE: if(*sport != *(uint16_t*)filter_value_p() && *dport != *(uint16_t*)filter_value_p()) { return true; } break; case CO_LT: if(*sport < *(uint16_t*)filter_value_p() || *dport < *(uint16_t*)filter_value_p()) { return true; } break; case CO_LE: if(*sport <= *(uint16_t*)filter_value_p() || *dport <= *(uint16_t*)filter_value_p()) { return true; } break; case CO_GT: if(*sport > *(uint16_t*)filter_value_p() || *dport > *(uint16_t*)filter_value_p()) { return true; } break; case CO_GE: if(*sport >= *(uint16_t*)filter_value_p() || *dport >= *(uint16_t*)filter_value_p()) { return true; } break; case CO_IN: if(flt_compare(m_cmpop, PT_PORT, sport, sizeof(*sport)) || flt_compare(m_cmpop, PT_PORT, dport, sizeof(*dport))) { return true; } break; default: throw sinsp_exception("filter error: unsupported port comparison operator"); } } return false; } bool sinsp_filter_check_fd::compare_domain(sinsp_evt *evt) { if(!extract_fd(evt)) { return false; } if(m_fdinfo != NULL) { scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type != SCAP_FD_IPV4_SOCK && evt_type != SCAP_FD_IPV6_SOCK) { return false; } if(m_fdinfo->is_role_none()) { return false; } uint32_t *addr; if(m_field_id == TYPE_CLIENTIP_NAME) { if(evt_type == SCAP_FD_IPV4_SOCK) { addr = &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip; } else { addr = &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip.m_b[0]; } } else if(m_field_id == TYPE_SERVERIP_NAME) { if(evt_type == SCAP_FD_IPV4_SOCK) { addr = &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip; } else { addr = &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip.m_b[0]; } } else { bool is_local; if(evt_type == SCAP_FD_IPV4_SOCK) { is_local = m_inspector->get_ifaddr_list()->is_ipv4addr_in_local_machine(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip, m_tinfo); } else { is_local = m_inspector->get_ifaddr_list()->is_ipv6addr_in_local_machine(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip, m_tinfo); } if(is_local) { if(m_field_id == TYPE_LIP_NAME) { if(evt_type == SCAP_FD_IPV4_SOCK) { addr = &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip; } else { addr = &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip.m_b[0]; } } else { if(evt_type == SCAP_FD_IPV4_SOCK) { addr = &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip; } else { addr = &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip.m_b[0]; } } } else { if(m_field_id == TYPE_LIP_NAME) { if(evt_type == SCAP_FD_IPV4_SOCK) { addr = &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip; } else { addr = &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip.m_b[0]; } } else { if(evt_type == SCAP_FD_IPV4_SOCK) { addr = &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip; } else { addr = &m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip.m_b[0]; } } } } uint64_t ts = evt->get_ts(); if(m_cmpop == CO_IN) { for (uint16_t i=0; i < m_val_storages.size(); i++) { if(sinsp_dns_manager::get().match((const char *)filter_value_p(i), (evt_type == SCAP_FD_IPV6_SOCK)? AF_INET6 : AF_INET, addr, ts)) { return true; } } return false; } else if(m_cmpop == CO_EQ) { return sinsp_dns_manager::get().match((const char *)filter_value_p(), (evt_type == SCAP_FD_IPV6_SOCK)? AF_INET6 : AF_INET, addr, ts); } else if(m_cmpop == CO_NE) { return !sinsp_dns_manager::get().match((const char *)filter_value_p(), (evt_type == SCAP_FD_IPV6_SOCK)? AF_INET6 : AF_INET, addr, ts); } else { throw sinsp_exception("filter error: fd.*ip.name filter only supports '=' and '!=' operators"); } } return false; } bool sinsp_filter_check_fd::extract_fd(sinsp_evt *evt) { ppm_event_flags eflags = evt->get_info_flags(); // // Make sure this is an event that creates or consumes an fd // if(eflags & (EF_CREATES_FD | EF_USES_FD | EF_DESTROYS_FD)) { // // This is an fd-related event, get the thread info and the fd info // m_tinfo = evt->get_thread_info(); if(m_tinfo == NULL) { return false; } m_fdinfo = evt->get_fd_info(); if(m_fdinfo == NULL && m_tinfo->m_lastevent_fd != -1) { m_fdinfo = m_tinfo->get_fd(m_tinfo->m_lastevent_fd); } // We'll check if fd is null below } else { return false; } return true; } bool sinsp_filter_check_fd::compare(sinsp_evt *evt) { // // Some fields are filter only and therefore get a special treatment // if(m_field_id == TYPE_IP) { return compare_ip(evt); } else if(m_field_id == TYPE_PORT || m_field_id == TYPE_PROTO) { return compare_port(evt); } else if(m_field_id == TYPE_NET) { return compare_net(evt); } // // Standard extract-based fields // uint32_t len = 0; bool sanitize_strings = false; uint8_t* extracted_val = extract(evt, &len, sanitize_strings); if(extracted_val == NULL) { // optimization for *_NAME fields // the first time we will call compare_domain, the next ones // we will the able to extract and use flt_compare if(m_field_id == TYPE_CLIENTIP_NAME || m_field_id == TYPE_SERVERIP_NAME || m_field_id == TYPE_LIP_NAME || m_field_id == TYPE_RIP_NAME) { return compare_domain(evt); } return false; } return flt_compare(m_cmpop, m_info.m_fields[m_field_id].m_type, extracted_val, len); } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_thread implementation /////////////////////////////////////////////////////////////////////////////// const filtercheck_field_info sinsp_filter_check_thread_fields[] = { {PT_INT64, EPF_NONE, PF_ID, "proc.pid", "the id of the process generating the event."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.exe", "the first command line argument (usually the executable name or a custom one)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.name", "the name (excluding the path) of the executable generating the event."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.args", "the arguments passed on the command line when starting the process generating the event."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.env", "the environment variables of the process generating the event."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.cmdline", "full process command line, i.e. proc.name + proc.args."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.exeline", "full process command line, with exe as first argument, i.e. proc.exe + proc.args."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.cwd", "the current working directory of the event."}, {PT_UINT32, EPF_NONE, PF_DEC, "proc.nthreads", "the number of threads that the process generating the event currently has, including the main process thread."}, {PT_UINT32, EPF_NONE, PF_DEC, "proc.nchilds", "the number of child threads that the process generating the event currently has. This excludes the main process thread."}, {PT_INT64, EPF_NONE, PF_ID, "proc.ppid", "the pid of the parent of the process generating the event."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.pname", "the name (excluding the path) of the parent of the process generating the event."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.pcmdline", "the full command line (proc.name + proc.args) of the parent of the process generating the event."}, {PT_INT64, EPF_NONE, PF_ID, "proc.apid", "the pid of one of the process ancestors. E.g. proc.apid[1] returns the parent pid, proc.apid[2] returns the grandparent pid, and so on. proc.apid[0] is the pid of the current process. proc.apid without arguments can be used in filters only and matches any of the process ancestors, e.g. proc.apid=1234."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.aname", "the name (excluding the path) of one of the process ancestors. E.g. proc.aname[1] returns the parent name, proc.aname[2] returns the grandparent name, and so on. proc.aname[0] is the name of the current process. proc.aname without arguments can be used in filters only and matches any of the process ancestors, e.g. proc.aname=bash."}, {PT_INT64, EPF_NONE, PF_ID, "proc.loginshellid", "the pid of the oldest shell among the ancestors of the current process, if there is one. This field can be used to separate different user sessions, and is useful in conjunction with chisels like spy_user."}, {PT_RELTIME, EPF_NONE, PF_DEC, "proc.duration", "number of nanoseconds since the process started."}, {PT_UINT64, EPF_NONE, PF_DEC, "proc.fdopencount", "number of open FDs for the process"}, {PT_INT64, EPF_NONE, PF_DEC, "proc.fdlimit", "maximum number of FDs the process can open."}, {PT_DOUBLE, EPF_NONE, PF_DEC, "proc.fdusage", "the ratio between open FDs and maximum available FDs for the process."}, {PT_UINT64, EPF_NONE, PF_DEC, "proc.vmsize", "total virtual memory for the process (as kb)."}, {PT_UINT64, EPF_NONE, PF_DEC, "proc.vmrss", "resident non-swapped memory for the process (as kb)."}, {PT_UINT64, EPF_NONE, PF_DEC, "proc.vmswap", "swapped memory for the process (as kb)."}, {PT_UINT64, EPF_NONE, PF_DEC, "thread.pfmajor", "number of major page faults since thread start."}, {PT_UINT64, EPF_NONE, PF_DEC, "thread.pfminor", "number of minor page faults since thread start."}, {PT_INT64, EPF_NONE, PF_ID, "thread.tid", "the id of the thread generating the event."}, {PT_BOOL, EPF_NONE, PF_NA, "thread.ismain", "'true' if the thread generating the event is the main one in the process."}, {PT_RELTIME, EPF_NONE, PF_DEC, "thread.exectime", "CPU time spent by the last scheduled thread, in nanoseconds. Exported by switch events only."}, {PT_RELTIME, EPF_NONE, PF_DEC, "thread.totexectime", "Total CPU time, in nanoseconds since the beginning of the capture, for the current thread. Exported by switch events only."}, {PT_CHARBUF, EPF_NONE, PF_NA, "thread.cgroups", "all the cgroups the thread belongs to, aggregated into a single string."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "thread.cgroup", "the cgroup the thread belongs to, for a specific subsystem. E.g. thread.cgroup.cpuacct."}, {PT_INT64, EPF_NONE, PF_ID, "thread.vtid", "the id of the thread generating the event as seen from its current PID namespace."}, {PT_INT64, EPF_NONE, PF_ID, "proc.vpid", "the id of the process generating the event as seen from its current PID namespace."}, {PT_DOUBLE, EPF_NONE, PF_NA, "thread.cpu", "the CPU consumed by the thread in the last second."}, {PT_DOUBLE, EPF_NONE, PF_NA, "thread.cpu.user", "the user CPU consumed by the thread in the last second."}, {PT_DOUBLE, EPF_NONE, PF_NA, "thread.cpu.system", "the system CPU consumed by the thread in the last second."}, {PT_UINT64, EPF_NONE, PF_DEC, "thread.vmsize", "For the process main thread, this is the total virtual memory for the process (as kb). For the other threads, this field is zero."}, {PT_UINT64, EPF_NONE, PF_DEC, "thread.vmrss", "For the process main thread, this is the resident non-swapped memory for the process (as kb). For the other threads, this field is zero."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "thread.vmsize.b", "For the process main thread, this is the total virtual memory for the process (in bytes). For the other threads, this field is zero."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "thread.vmrss.b", "For the process main thread, this is the resident non-swapped memory for the process (in bytes). For the other threads, this field is zero."}, {PT_INT64, EPF_NONE, PF_ID, "proc.sid", "the session id of the process generating the event."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.sname", "the name of the current process's session leader. This is either the process with pid=proc.sid or the eldest ancestor that has the same sid as the current process."}, {PT_INT32, EPF_NONE, PF_ID, "proc.tty", "The controlling terminal of the process. 0 for processes without a terminal."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.exepath", "The full executable path of the process."}, {PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "thread.nametid", "this field chains the process name and tid of a thread and can be used as a specific identifier of a thread for a specific execve."}, {PT_INT64, EPF_NONE, PF_ID, "proc.vpgid", "the process group id of the process generating the event, as seen from its current PID namespace."}, {PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_healthcheck", "true if this process is running as a part of the container's health check."}, {PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_liveness_probe", "true if this process is running as a part of the container's liveness probe."}, {PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_readiness_probe", "true if this process is running as a part of the container's readiness probe."}, }; sinsp_filter_check_thread::sinsp_filter_check_thread() { m_info.m_name = "process"; m_info.m_fields = sinsp_filter_check_thread_fields; m_info.m_nfields = sizeof(sinsp_filter_check_thread_fields) / sizeof(sinsp_filter_check_thread_fields[0]); m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; m_u64val = 0; m_cursec_ts = 0; } sinsp_filter_check* sinsp_filter_check_thread::allocate_new() { return (sinsp_filter_check*) new sinsp_filter_check_thread(); } int32_t sinsp_filter_check_thread::extract_arg(string fldname, string val, OUT const struct ppm_param_info** parinfo) { uint32_t parsed_len = 0; // // 'arg' and 'resarg' are handled in a custom way // if(m_field_id == TYPE_APID || m_field_id == TYPE_ANAME) { if(val[fldname.size()] == '[') { parsed_len = (uint32_t)val.find(']'); string numstr = val.substr(fldname.size() + 1, parsed_len - fldname.size() - 1); m_argid = sinsp_numparser::parsed32(numstr); parsed_len++; } else { throw sinsp_exception("filter syntax error: " + val); } } else if(m_field_id == TYPE_CGROUP) { if(val[fldname.size()] == '.') { size_t endpos; for(endpos = fldname.size() + 1; endpos < val.length(); ++endpos) { if(!isalpha(val[endpos]) && val[endpos] != '_') { break; } } parsed_len = (uint32_t)endpos; m_argname = val.substr(fldname.size() + 1, endpos - fldname.size() - 1); } else { throw sinsp_exception("filter syntax error: " + val); } } return parsed_len; } int32_t sinsp_filter_check_thread::parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) { string val(str); if(string(val, 0, sizeof("arg") - 1) == "arg") { // // 'arg' is handled in a custom way // throw sinsp_exception("filter error: proc.arg filter not implemented yet"); } else if(string(val, 0, sizeof("proc.apid") - 1) == "proc.apid") { m_field_id = TYPE_APID; m_field = &m_info.m_fields[m_field_id]; int32_t res = 0; try { res = extract_arg("proc.apid", val, NULL); } catch(...) { if(val == "proc.apid") { m_argid = -1; res = (int32_t)val.size(); } } return res; } else if(string(val, 0, sizeof("proc.aname") - 1) == "proc.aname") { m_field_id = TYPE_ANAME; m_field = &m_info.m_fields[m_field_id]; int32_t res = 0; try { res = extract_arg("proc.aname", val, NULL); } catch(...) { if(val == "proc.aname") { m_argid = -1; res = (int32_t)val.size(); } } return res; } else if(string(val, 0, sizeof("thread.totexectime") - 1) == "thread.totexectime") { // // Allocate thread storage for the value // if(alloc_state) { m_th_state_id = m_inspector->reserve_thread_memory(sizeof(uint64_t)); } return sinsp_filter_check::parse_field_name(str, alloc_state, needed_for_filtering); } else if(string(val, 0, sizeof("thread.cgroup") - 1) == "thread.cgroup" && string(val, 0, sizeof("thread.cgroups") - 1) != "thread.cgroups") { m_field_id = TYPE_CGROUP; m_field = &m_info.m_fields[m_field_id]; return extract_arg("thread.cgroup", val, NULL); } else if(string(val, 0, sizeof("thread.cpu") - 1) == "thread.cpu") { if(alloc_state) { m_th_state_id = m_inspector->reserve_thread_memory(sizeof(uint64_t)); } return sinsp_filter_check::parse_field_name(str, alloc_state, needed_for_filtering); } else { return sinsp_filter_check::parse_field_name(str, alloc_state, needed_for_filtering); } } uint64_t sinsp_filter_check_thread::extract_exectime(sinsp_evt *evt) { uint64_t res = 0; if(m_last_proc_switch_times.size() == 0) { // // Initialize the vector of CPU times // const scap_machine_info* minfo = m_inspector->get_machine_info(); ASSERT(minfo->num_cpus != 0); for(uint32_t j = 0; j < minfo->num_cpus; j++) { m_last_proc_switch_times.push_back(0); } } uint32_t cpuid = evt->get_cpuid(); uint64_t ts = evt->get_ts(); uint64_t lasttime = m_last_proc_switch_times[cpuid]; if(lasttime != 0) { res = ts - lasttime; } ASSERT(cpuid < m_last_proc_switch_times.size()); m_last_proc_switch_times[cpuid] = ts; return res; } uint8_t* sinsp_filter_check_thread::extract_thread_cpu(sinsp_evt *evt, OUT uint32_t* len, sinsp_threadinfo* tinfo, bool extract_user, bool extract_system) { uint16_t etype = evt->get_type(); if(etype == PPME_PROCINFO_E) { uint64_t user = 0; uint64_t system = 0; uint64_t tcpu; if(extract_user) { sinsp_evt_param* parinfo = evt->get_param(0); user = *(uint64_t*)parinfo->m_val; } if(extract_system) { sinsp_evt_param* parinfo = evt->get_param(1); system = *(uint64_t*)parinfo->m_val; } tcpu = user + system; uint64_t* last_t_tot_cpu = (uint64_t*)tinfo->get_private_state(m_th_state_id); if(*last_t_tot_cpu != 0) { uint64_t deltaval = tcpu - *last_t_tot_cpu; m_dval = (double)deltaval;// / (ONE_SECOND_IN_NS / 100); if(m_dval > 100) { m_dval = 100; } } else { m_dval = 0; } *last_t_tot_cpu = tcpu; RETURN_EXTRACT_VAR(m_dval); } return NULL; } uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = 0; sinsp_threadinfo* tinfo = evt->get_thread_info(); if(tinfo == NULL && m_field_id != TYPE_TID && m_field_id != TYPE_EXECTIME && m_field_id != TYPE_TOTEXECTIME) { return NULL; } switch(m_field_id) { case TYPE_TID: m_u64val = evt->get_tid(); RETURN_EXTRACT_VAR(m_u64val); case TYPE_PID: RETURN_EXTRACT_VAR(tinfo->m_pid); case TYPE_SID: RETURN_EXTRACT_VAR(tinfo->m_sid); case TYPE_VPGID: RETURN_EXTRACT_VAR(tinfo->m_vpgid); case TYPE_SNAME: { // // Relying on the convention that a session id is the process id of the session leader // sinsp_threadinfo* sinfo = m_inspector->get_thread(tinfo->m_sid, false, true); if(sinfo != NULL) { m_tstr = sinfo->get_comm(); RETURN_EXTRACT_STRING(m_tstr); } else { // This can occur when the session leader process has exited. // Find the highest ancestor process that has the same session id and // declare it to be the session leader. sinsp_threadinfo* mt = tinfo->get_main_thread(); if(mt == NULL) { return NULL; } int64_t sid = mt->m_sid; sinsp_threadinfo::visitor_func_t visitor = [sid, &mt] (sinsp_threadinfo *pt) { if(pt->m_sid != sid) { return false; } mt = pt; return true; }; mt->traverse_parent_state(visitor); // mt has been updated to the highest process that has the same session id. // mt's comm is considered the session leader. m_tstr = mt->get_comm(); RETURN_EXTRACT_STRING(m_tstr); } } case TYPE_TTY: RETURN_EXTRACT_VAR(tinfo->m_tty); case TYPE_NAME: m_tstr = tinfo->get_comm(); RETURN_EXTRACT_STRING(m_tstr); case TYPE_EXE: m_tstr = tinfo->get_exe(); RETURN_EXTRACT_STRING(m_tstr); case TYPE_EXEPATH: m_tstr = tinfo->get_exepath(); RETURN_EXTRACT_STRING(m_tstr); case TYPE_ARGS: { m_tstr.clear(); uint32_t j; uint32_t nargs = (uint32_t)tinfo->m_args.size(); for(j = 0; j < nargs; j++) { m_tstr += tinfo->m_args[j]; if(j < nargs -1) { m_tstr += ' '; } } RETURN_EXTRACT_STRING(m_tstr); } case TYPE_ENV: { m_tstr.clear(); uint32_t j; const auto& env = tinfo->get_env(); uint32_t nargs = (uint32_t)env.size(); for(j = 0; j < nargs; j++) { m_tstr += env[j]; if(j < nargs -1) { m_tstr += ' '; } } RETURN_EXTRACT_STRING(m_tstr); } case TYPE_CMDLINE: { sinsp_threadinfo::populate_cmdline(m_tstr, tinfo); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_EXELINE: { m_tstr = tinfo->get_exe() + " "; uint32_t j; uint32_t nargs = (uint32_t)tinfo->m_args.size(); for(j = 0; j < nargs; j++) { m_tstr += tinfo->m_args[j]; if(j < nargs -1) { m_tstr += ' '; } } RETURN_EXTRACT_STRING(m_tstr); } case TYPE_CWD: m_tstr = tinfo->get_cwd(); RETURN_EXTRACT_STRING(m_tstr); case TYPE_NTHREADS: { sinsp_threadinfo* ptinfo = tinfo->get_main_thread(); if(ptinfo) { m_u64val = ptinfo->m_nchilds + 1; RETURN_EXTRACT_VAR(m_u64val); } else { ASSERT(false); return NULL; } } case TYPE_NCHILDS: RETURN_EXTRACT_VAR(tinfo->m_nchilds); case TYPE_ISMAINTHREAD: m_tbool = (uint32_t)tinfo->is_main_thread(); RETURN_EXTRACT_VAR(m_tbool); case TYPE_EXECTIME: { m_u64val = 0; uint16_t etype = evt->get_type(); if(etype == PPME_SCHEDSWITCH_1_E || etype == PPME_SCHEDSWITCH_6_E) { m_u64val = extract_exectime(evt); } RETURN_EXTRACT_VAR(m_u64val); } case TYPE_TOTEXECTIME: { m_u64val = 0; uint16_t etype = evt->get_type(); if(etype == PPME_SCHEDSWITCH_1_E || etype == PPME_SCHEDSWITCH_6_E) { m_u64val = extract_exectime(evt); } sinsp_threadinfo* tinfo = evt->get_thread_info(false); if(tinfo != NULL) { uint64_t* ptot = (uint64_t*)tinfo->get_private_state(m_th_state_id); *ptot += m_u64val; RETURN_EXTRACT_PTR(ptot); } else { return NULL; } } case TYPE_PPID: if(tinfo->is_main_thread()) { RETURN_EXTRACT_VAR(tinfo->m_ptid); } else { sinsp_threadinfo* mt = tinfo->get_main_thread(); if(mt != NULL) { RETURN_EXTRACT_VAR(mt->m_ptid); } else { return NULL; } } case TYPE_PNAME: { sinsp_threadinfo* ptinfo = m_inspector->get_thread(tinfo->m_ptid, false, true); if(ptinfo != NULL) { m_tstr = ptinfo->get_comm(); RETURN_EXTRACT_STRING(m_tstr); } else { return NULL; } } case TYPE_PCMDLINE: { sinsp_threadinfo* ptinfo = m_inspector->get_thread(tinfo->m_ptid, false, true); if(ptinfo != NULL) { sinsp_threadinfo::populate_cmdline(m_tstr, ptinfo); RETURN_EXTRACT_STRING(m_tstr); } else { return NULL; } } case TYPE_APID: { sinsp_threadinfo* mt = NULL; if(tinfo->is_main_thread()) { mt = tinfo; } else { mt = tinfo->get_main_thread(); if(mt == NULL) { return NULL; } } // // Search for a specific ancestors // for(int32_t j = 0; j < m_argid; j++) { mt = mt->get_parent_thread(); if(mt == NULL) { return NULL; } } RETURN_EXTRACT_VAR(mt->m_pid); } case TYPE_ANAME: { sinsp_threadinfo* mt = NULL; if(tinfo->is_main_thread()) { mt = tinfo; } else { mt = tinfo->get_main_thread(); if(mt == NULL) { return NULL; } } for(int32_t j = 0; j < m_argid; j++) { mt = mt->get_parent_thread(); if(mt == NULL) { return NULL; } } m_tstr = mt->get_comm(); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_LOGINSHELLID: { sinsp_threadinfo* mt = NULL; int64_t* res = NULL; if(tinfo->is_main_thread()) { mt = tinfo; } else { mt = tinfo->get_main_thread(); if(mt == NULL) { return NULL; } } sinsp_threadinfo::visitor_func_t check_thread_for_shell = [&res] (sinsp_threadinfo *pt) { size_t len = pt->m_comm.size(); if(len >= 2 && pt->m_comm[len - 2] == 's' && pt->m_comm[len - 1] == 'h') { res = &pt->m_pid; } return true; }; // First call the visitor on the main thread. check_thread_for_shell(mt); // Then check all its parents to see if they are shells mt->traverse_parent_state(check_thread_for_shell); RETURN_EXTRACT_PTR(res); } case TYPE_DURATION: if(tinfo->m_clone_ts != 0) { m_s64val = evt->get_ts() - tinfo->m_clone_ts; ASSERT(m_s64val > 0); RETURN_EXTRACT_VAR(m_s64val); } else { return NULL; } case TYPE_FDOPENCOUNT: m_u64val = tinfo->get_fd_opencount(); RETURN_EXTRACT_VAR(m_u64val); case TYPE_FDLIMIT: m_s64val = tinfo->get_fd_limit(); RETURN_EXTRACT_VAR(m_s64val); case TYPE_FDUSAGE: m_dval = tinfo->get_fd_usage_pct_d(); RETURN_EXTRACT_VAR(m_dval); case TYPE_VMSIZE: m_u64val = tinfo->m_vmsize_kb; RETURN_EXTRACT_VAR(m_u64val); case TYPE_VMRSS: m_u64val = tinfo->m_vmrss_kb; RETURN_EXTRACT_VAR(m_u64val); case TYPE_VMSWAP: m_u64val = tinfo->m_vmswap_kb; RETURN_EXTRACT_VAR(m_u64val); case TYPE_THREAD_VMSIZE: if(tinfo->is_main_thread()) { m_u64val = tinfo->m_vmsize_kb; } else { m_u64val = 0; } RETURN_EXTRACT_VAR(m_u64val); case TYPE_THREAD_VMRSS: if(tinfo->is_main_thread()) { m_u64val = tinfo->m_vmrss_kb; } else { m_u64val = 0; } RETURN_EXTRACT_VAR(m_u64val); case TYPE_THREAD_VMSIZE_B: if(tinfo->is_main_thread()) { m_u64val = tinfo->m_vmsize_kb * 1024; } else { m_u64val = 0; } RETURN_EXTRACT_VAR(m_u64val); case TYPE_THREAD_VMRSS_B: if(tinfo->is_main_thread()) { m_u64val = tinfo->m_vmrss_kb * 1024; } else { m_u64val = 0; } RETURN_EXTRACT_VAR(m_u64val); case TYPE_PFMAJOR: m_u64val = tinfo->m_pfmajor; RETURN_EXTRACT_VAR(m_u64val); case TYPE_PFMINOR: m_u64val = tinfo->m_pfminor; RETURN_EXTRACT_VAR(m_u64val); case TYPE_CGROUPS: { m_tstr.clear(); uint32_t j; uint32_t nargs = (uint32_t)tinfo->m_cgroups.size(); if(nargs == 0) { return NULL; } for(j = 0; j < nargs; j++) { m_tstr += tinfo->m_cgroups[j].first; m_tstr += "="; m_tstr += tinfo->m_cgroups[j].second; if(j < nargs - 1) { m_tstr += ' '; } } RETURN_EXTRACT_STRING(m_tstr); } case TYPE_CGROUP: { uint32_t nargs = (uint32_t)tinfo->m_cgroups.size(); if(nargs == 0) { return NULL; } for(uint32_t j = 0; j < nargs; j++) { if(tinfo->m_cgroups[j].first == m_argname) { m_tstr = tinfo->m_cgroups[j].second; RETURN_EXTRACT_STRING(m_tstr); } } return NULL; } case TYPE_VTID: if(tinfo->m_vtid == -1) { return NULL; } m_u64val = tinfo->m_vtid; RETURN_EXTRACT_VAR(m_u64val); case TYPE_VPID: if(tinfo->m_vpid == -1) { return NULL; } m_u64val = tinfo->m_vpid; RETURN_EXTRACT_VAR(m_u64val); /* case TYPE_PROC_CPU: { uint16_t etype = evt->get_type(); if(etype == PPME_PROCINFO_E) { double thval; uint64_t tcpu; sinsp_evt_param* parinfo = evt->get_param(0); tcpu = *(uint64_t*)parinfo->m_val; parinfo = evt->get_param(1); tcpu += *(uint64_t*)parinfo->m_val; if(tinfo->m_last_t_tot_cpu != 0) { uint64_t deltaval = tcpu - tinfo->m_last_t_tot_cpu; thval = (double)deltaval;// / (ONE_SECOND_IN_NS / 100); if(thval > 100) { thval = 100; } } else { thval = 0; } tinfo->m_last_t_tot_cpu = tcpu; uint64_t ets = evt->get_ts(); sinsp_threadinfo* mt = tinfo->get_main_thread(); if(ets != mt->m_last_mt_cpu_ts) { mt->m_last_mt_tot_cpu = 0; mt->m_last_mt_cpu_ts = ets; } mt->m_last_mt_tot_cpu += thval; m_dval = mt->m_last_mt_tot_cpu; RETURN_EXTRACT_VAR(m_dval); } return NULL; } */ case TYPE_THREAD_CPU: { return extract_thread_cpu(evt, len, tinfo, true, true); } case TYPE_THREAD_CPU_USER: { return extract_thread_cpu(evt, len, tinfo, true, false); } case TYPE_THREAD_CPU_SYSTEM: { return extract_thread_cpu(evt, len, tinfo, false, true); } case TYPE_NAMETID: m_tstr = tinfo->get_comm() + to_string(evt->get_tid()); RETURN_EXTRACT_STRING(m_tstr); case TYPE_IS_CONTAINER_HEALTHCHECK: m_tbool = (tinfo->m_category == sinsp_threadinfo::CAT_HEALTHCHECK); RETURN_EXTRACT_VAR(m_tbool); case TYPE_IS_CONTAINER_LIVENESS_PROBE: m_tbool = (tinfo->m_category == sinsp_threadinfo::CAT_LIVENESS_PROBE); RETURN_EXTRACT_VAR(m_tbool); case TYPE_IS_CONTAINER_READINESS_PROBE: m_tbool = (tinfo->m_category == sinsp_threadinfo::CAT_READINESS_PROBE); RETURN_EXTRACT_VAR(m_tbool); default: ASSERT(false); return NULL; } } bool sinsp_filter_check_thread::compare_full_apid(sinsp_evt *evt) { sinsp_threadinfo* tinfo = evt->get_thread_info(); if(tinfo == NULL) { return false; } sinsp_threadinfo* mt = NULL; if(tinfo->is_main_thread()) { mt = tinfo; } else { mt = tinfo->get_main_thread(); if(mt == NULL) { return false; } } // // No id specified, search in all of the ancestors // bool found = false; sinsp_threadinfo::visitor_func_t visitor = [this, &found] (sinsp_threadinfo *pt) { bool res; res = flt_compare(m_cmpop, PT_PID, &pt->m_pid); if(res == true) { found = true; // Can stop traversing parent state return false; } return true; }; mt->traverse_parent_state(visitor); return found; } bool sinsp_filter_check_thread::compare_full_aname(sinsp_evt *evt) { sinsp_threadinfo* tinfo = evt->get_thread_info(); if(tinfo == NULL) { return false; } sinsp_threadinfo* mt = NULL; if(tinfo->is_main_thread()) { mt = tinfo; } else { mt = tinfo->get_main_thread(); if(mt == NULL) { return false; } } // // No id specified, search in all of the ancestors // bool found = false; sinsp_threadinfo::visitor_func_t visitor = [this, &found] (sinsp_threadinfo *pt) { bool res; res = flt_compare(m_cmpop, PT_CHARBUF, (void*)pt->m_comm.c_str()); if(res == true) { found = true; // Can stop traversing parent state return false; } return true; }; mt->traverse_parent_state(visitor); return found; } bool sinsp_filter_check_thread::compare(sinsp_evt *evt) { if(m_field_id == TYPE_APID) { if(m_argid == -1) { return compare_full_apid(evt); } } else if(m_field_id == TYPE_ANAME) { if(m_argid == -1) { return compare_full_aname(evt); } } return sinsp_filter_check::compare(evt); } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_event implementation /////////////////////////////////////////////////////////////////////////////// const filtercheck_field_info sinsp_filter_check_event_fields[] = { {PT_UINT64, EPF_NONE, PF_ID, "evt.num", "event number."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.time", "event timestamp as a time string that includes the nanosecond part."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.time.s", "event timestamp as a time string with no nanoseconds."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.time.iso8601", "event timestamp in ISO 8601 format, including nanoseconds and time zone offset (in UTC)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.datetime", "event timestamp as a time string that includes the date."}, {PT_ABSTIME, EPF_NONE, PF_DEC, "evt.rawtime", "absolute event timestamp, i.e. nanoseconds from epoch."}, {PT_ABSTIME, EPF_NONE, PF_DEC, "evt.rawtime.s", "integer part of the event timestamp (e.g. seconds since epoch)."}, {PT_ABSTIME, EPF_NONE, PF_10_PADDED_DEC, "evt.rawtime.ns", "fractional part of the absolute event timestamp."}, {PT_RELTIME, EPF_NONE, PF_10_PADDED_DEC, "evt.reltime", "number of nanoseconds from the beginning of the capture."}, {PT_RELTIME, EPF_NONE, PF_DEC, "evt.reltime.s", "number of seconds from the beginning of the capture."}, {PT_RELTIME, EPF_NONE, PF_10_PADDED_DEC, "evt.reltime.ns", "fractional part (in ns) of the time from the beginning of the capture."}, {PT_RELTIME, EPF_NONE, PF_DEC, "evt.latency", "delta between an exit event and the correspondent enter event, in nanoseconds."}, {PT_RELTIME, EPF_NONE, PF_DEC, "evt.latency.s", "integer part of the event latency delta."}, {PT_RELTIME, EPF_NONE, PF_10_PADDED_DEC, "evt.latency.ns", "fractional part of the event latency delta."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.latency.quantized", "10-base log of the delta between an exit event and the correspondent enter event."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.latency.human", "delta between an exit event and the correspondent enter event, as a human readable string (e.g. 10.3ms)."}, {PT_RELTIME, EPF_NONE, PF_DEC, "evt.deltatime", "delta between this event and the previous event, in nanoseconds."}, {PT_RELTIME, EPF_NONE, PF_DEC, "evt.deltatime.s", "integer part of the delta between this event and the previous event."}, {PT_RELTIME, EPF_NONE, PF_10_PADDED_DEC, "evt.deltatime.ns", "fractional part of the delta between this event and the previous event."}, {PT_CHARBUF, EPF_PRINT_ONLY, PF_NA, "evt.outputtime", "this depends on -t param, default is %evt.time ('h')."}, {PT_CHARBUF, EPF_NONE, PF_DIR, "evt.dir", "event direction can be either '>' for enter events or '<' for exit events."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.type", "The name of the event (e.g. 'open')."}, {PT_UINT32, EPF_REQUIRES_ARGUMENT, PF_NA, "evt.type.is", "allows one to specify an event type, and returns 1 for events that are of that type. For example, evt.type.is.open returns 1 for open events, 0 for any other event."}, {PT_CHARBUF, EPF_NONE, PF_NA, "syscall.type", "For system call events, the name of the system call (e.g. 'open'). Unset for other events (e.g. switch or sysdig internal events). Use this field instead of evt.type if you need to make sure that the filtered/printed value is actually a system call."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.category", "The event category. Example values are 'file' (for file operations like open and close), 'net' (for network operations like socket and bind), memory (for things like brk or mmap), and so on."}, {PT_INT16, EPF_NONE, PF_ID, "evt.cpu", "number of the CPU where this event happened."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.args", "all the event arguments, aggregated into a single string."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "evt.arg", "one of the event arguments specified by name or by number. Some events (e.g. return codes or FDs) will be converted into a text representation when possible. E.g. 'evt.arg.fd' or 'evt.arg[0]'."}, {PT_DYN, EPF_REQUIRES_ARGUMENT, PF_NA, "evt.rawarg", "one of the event arguments specified by name. E.g. 'evt.rawarg.fd'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.info", "for most events, this field returns the same value as evt.args. However, for some events (like writes to /dev/log) it provides higher level information coming from decoding the arguments."}, {PT_BYTEBUF, EPF_NONE, PF_NA, "evt.buffer", "the binary data buffer for events that have one, like read(), recvfrom(), etc. Use this field in filters with 'contains' to search into I/O data buffers."}, {PT_UINT64, EPF_NONE, PF_DEC, "evt.buflen", "the length of the binary data buffer for events that have one, like read(), recvfrom(), etc."}, {PT_CHARBUF, EPF_NONE, PF_DEC, "evt.res", "event return value, as a string. If the event failed, the result is an error code string (e.g. 'ENOENT'), otherwise the result is the string 'SUCCESS'."}, {PT_INT64, EPF_NONE, PF_DEC, "evt.rawres", "event return value, as a number (e.g. -2). Useful for range comparisons."}, {PT_BOOL, EPF_NONE, PF_NA, "evt.failed", "'true' for events that returned an error status."}, {PT_BOOL, EPF_NONE, PF_NA, "evt.is_io", "'true' for events that read or write to FDs, like read(), send, recvfrom(), etc."}, {PT_BOOL, EPF_NONE, PF_NA, "evt.is_io_read", "'true' for events that read from FDs, like read(), recv(), recvfrom(), etc."}, {PT_BOOL, EPF_NONE, PF_NA, "evt.is_io_write", "'true' for events that write to FDs, like write(), send(), etc."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.io_dir", "'r' for events that read from FDs, like read(); 'w' for events that write to FDs, like write()."}, {PT_BOOL, EPF_NONE, PF_NA, "evt.is_wait", "'true' for events that make the thread wait, e.g. sleep(), select(), poll()."}, {PT_RELTIME, EPF_NONE, PF_DEC, "evt.wait_latency", "for events that make the thread wait (e.g. sleep(), select(), poll()), this is the time spent waiting for the event to return, in nanoseconds."}, {PT_BOOL, EPF_NONE, PF_NA, "evt.is_syslog", "'true' for events that are writes to /dev/log."}, {PT_UINT32, EPF_NONE, PF_DEC, "evt.count", "This filter field always returns 1 and can be used to count events from inside chisels."}, {PT_UINT32, EPF_NONE, PF_DEC, "evt.count.error", "This filter field returns 1 for events that returned with an error, and can be used to count event failures from inside chisels."}, {PT_UINT32, EPF_NONE, PF_DEC, "evt.count.error.file", "This filter field returns 1 for events that returned with an error and are related to file I/O, and can be used to count event failures from inside chisels."}, {PT_UINT32, EPF_NONE, PF_DEC, "evt.count.error.net", "This filter field returns 1 for events that returned with an error and are related to network I/O, and can be used to count event failures from inside chisels."}, {PT_UINT32, EPF_NONE, PF_DEC, "evt.count.error.memory", "This filter field returns 1 for events that returned with an error and are related to memory allocation, and can be used to count event failures from inside chisels."}, {PT_UINT32, EPF_NONE, PF_DEC, "evt.count.error.other", "This filter field returns 1 for events that returned with an error and are related to none of the previous categories, and can be used to count event failures from inside chisels."}, {PT_UINT32, EPF_NONE, PF_DEC, "evt.count.exit", "This filter field returns 1 for exit events, and can be used to count single events from inside chisels."}, {PT_UINT32, EPF_TABLE_ONLY, PF_DEC, "evt.count.procinfo", "This filter field returns 1 for procinfo events generated by process main threads, and can be used to count processes from inside views."}, {PT_UINT32, EPF_TABLE_ONLY, PF_DEC, "evt.count.threadinfo", "This filter field returns 1 for procinfo events, and can be used to count processes from inside views."}, {PT_UINT64, (filtercheck_field_flags) (EPF_FILTER_ONLY | EPF_REQUIRES_ARGUMENT), PF_DEC, "evt.around", "Accepts the event if it's around the specified time interval. The syntax is evt.around[T]=D, where T is the value returned by %evt.rawtime for the event and D is a delta in milliseconds. For example, evt.around[1404996934793590564]=1000 will return the events with timestamp with one second before the timestamp and one second after it, for a total of two seconds of capture."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "evt.abspath", "Absolute path calculated from dirfd and name during syscalls like renameat and symlinkat. Use 'evt.abspath.src' or 'evt.abspath.dst' for syscalls that support multiple paths."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.buflen.in", "the length of the binary data buffer, but only for input I/O events."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.buflen.out", "the length of the binary data buffer, but only for output I/O events."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.buflen.file", "the length of the binary data buffer, but only for file I/O events."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.buflen.file.in", "the length of the binary data buffer, but only for input file I/O events."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.buflen.file.out", "the length of the binary data buffer, but only for output file I/O events."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.buflen.net", "the length of the binary data buffer, but only for network I/O events."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.buflen.net.in", "the length of the binary data buffer, but only for input network I/O events."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "evt.buflen.net.out", "the length of the binary data buffer, but only for output network I/O events."}, {PT_BOOL, EPF_NONE, PF_NA, "evt.is_open_read", "'true' for open/openat events where the path was opened for reading"}, {PT_BOOL, EPF_NONE, PF_NA, "evt.is_open_write", "'true' for open/openat events where the path was opened for writing"}, {PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "evt.infra.docker.name", "for docker infrastructure events, the name of the event."}, {PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "evt.infra.docker.container.id", "for docker infrastructure events, the id of the impacted container."}, {PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "evt.infra.docker.container.name", "for docker infrastructure events, the name of the impacted container."}, {PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "evt.infra.docker.container.image", "for docker infrastructure events, the image name of the impacted container."}, }; sinsp_filter_check_event::sinsp_filter_check_event() { m_is_compare = false; m_info.m_name = "evt"; m_info.m_fields = sinsp_filter_check_event_fields; m_info.m_nfields = sizeof(sinsp_filter_check_event_fields) / sizeof(sinsp_filter_check_event_fields[0]); m_u64val = 0; m_converter = new sinsp_filter_check_reference(); m_storage_size = UESTORAGE_INITIAL_BUFSIZE; m_storage = (char*)malloc(m_storage_size); if(m_storage == NULL) { throw sinsp_exception("memory allocation error in sinsp_filter_check_appevt::sinsp_filter_check_event"); } m_cargname = NULL; } sinsp_filter_check_event::~sinsp_filter_check_event() { if(m_storage != NULL) { free(m_storage); } if(m_converter != NULL) { delete m_converter; } } sinsp_filter_check* sinsp_filter_check_event::allocate_new() { return (sinsp_filter_check*) new sinsp_filter_check_event(); } int32_t sinsp_filter_check_event::extract_arg(string fldname, string val, OUT const struct ppm_param_info** parinfo) { uint32_t parsed_len = 0; // // 'arg' and 'resarg' are handled in a custom way // if(val[fldname.size()] == '[') { if(parinfo != NULL) { throw sinsp_exception("evt.arg fields must be expressed explicitly"); } parsed_len = (uint32_t)val.find(']'); string numstr = val.substr(fldname.size() + 1, parsed_len - fldname.size() - 1); if(m_field_id == TYPE_AROUND) { m_u64val = sinsp_numparser::parseu64(numstr); } else { m_argid = sinsp_numparser::parsed32(numstr); } parsed_len++; } else if(val[fldname.size()] == '.') { if(m_field_id == TYPE_AROUND) { throw sinsp_exception("wrong syntax for evt.around"); } const struct ppm_param_info* pi = sinsp_utils::find_longest_matching_evt_param(val.substr(fldname.size() + 1)); if(pi == NULL) { throw sinsp_exception("unknown event argument " + val.substr(fldname.size() + 1)); } m_argname = pi->name; parsed_len = (uint32_t)(fldname.size() + strlen(pi->name) + 1); m_argid = -1; if(parinfo != NULL) { *parinfo = pi; } } else { throw sinsp_exception("filter syntax error: " + val); } return parsed_len; } int32_t sinsp_filter_check_event::extract_type(string fldname, string val, OUT const struct ppm_param_info** parinfo) { uint32_t parsed_len = 0; if(val[fldname.size()] == '.') { string itype = val.substr(fldname.size() + 1); if(sinsp_numparser::tryparseu32(itype, &m_evtid)) { m_evtid1 = PPM_EVENT_MAX; parsed_len = (uint32_t)(fldname.size() + itype.size() + 1); return parsed_len; } for(uint32_t j = 0; j < PPM_EVENT_MAX; j++) { const ppm_event_info* ei = &g_infotables.m_event_info[j]; if(itype == ei->name) { m_evtid = j; m_evtid1 = j + 1; parsed_len = (uint32_t)(fldname.size() + strlen(ei->name) + 1); break; } } } else { throw sinsp_exception("filter syntax error: " + val); } return parsed_len; } int32_t sinsp_filter_check_event::parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) { string val(str); int32_t res = 0; // // A couple of fields are handled in a custom way // if(string(val, 0, sizeof("evt.arg") - 1) == "evt.arg" && string(val, 0, sizeof("evt.args") - 1) != "evt.args") { m_field_id = TYPE_ARGSTR; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("evt.arg", val, NULL); } else if(string(val, 0, sizeof("evt.rawarg") - 1) == "evt.rawarg") { m_field_id = TYPE_ARGRAW; m_customfield = m_info.m_fields[m_field_id]; m_field = &m_customfield; res = extract_arg("evt.rawarg", val, &m_arginfo); m_customfield.m_type = m_arginfo->type; } else if(string(val, 0, sizeof("evt.around") - 1) == "evt.around") { m_field_id = TYPE_AROUND; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("evt.around", val, NULL); } else if(string(val, 0, sizeof("evt.latency") - 1) == "evt.latency" || string(val, 0, sizeof("evt.latency.s") - 1) == "evt.latency.s" || string(val, 0, sizeof("evt.latency.ns") - 1) == "evt.latency.ns" || string(val, 0, sizeof("evt.latency.quantized") - 1) == "evt.latency.quantized" || string(val, 0, sizeof("evt.latency.human") - 1) == "evt.latency.human") { // // These fields need to store the previuos event type in the thread state // if(alloc_state) { m_th_state_id = m_inspector->reserve_thread_memory(sizeof(uint16_t)); } res = sinsp_filter_check::parse_field_name(str, alloc_state, needed_for_filtering); } else if(string(val, 0, sizeof("evt.abspath") - 1) == "evt.abspath") { m_field_id = TYPE_ABSPATH; m_field = &m_info.m_fields[m_field_id]; if(string(val, 0, sizeof("evt.abspath.src") - 1) == "evt.abspath.src") { m_argid = 1; res = sizeof("evt.abspath.src") - 1; } else if(string(val, 0, sizeof("evt.abspath.dst") - 1) == "evt.abspath.dst") { m_argid = 2; res = sizeof("evt.abspath.dst") - 1; } else { m_argid = 0; res = sizeof("evt.abspath") - 1; } } else if(string(val, 0, sizeof("evt.type.is") - 1) == "evt.type.is") { m_field_id = TYPE_TYPE_IS; m_field = &m_info.m_fields[m_field_id]; res = extract_type("evt.type.is", val, NULL); } else { res = sinsp_filter_check::parse_field_name(str, alloc_state, needed_for_filtering); } return res; } size_t sinsp_filter_check_event::parse_filter_value(const char* str, uint32_t len, uint8_t *storage, uint32_t storage_len) { size_t parsed_len; if(m_field_id == sinsp_filter_check_event::TYPE_ARGRAW) { ASSERT(m_arginfo != NULL); parsed_len = sinsp_filter_value_parser::string_to_rawval(str, len, filter_value_p(), filter_value().size(), m_arginfo->type); } else { parsed_len = sinsp_filter_check::parse_filter_value(str, len, storage, storage_len); } return parsed_len; } void sinsp_filter_check_event::validate_filter_value(const char* str, uint32_t len) { string val(str); if(m_field_id == TYPE_TYPE) { sinsp_evttables* einfo = m_inspector->get_event_info_tables(); const struct ppm_event_info* etable = einfo->m_event_info; const struct ppm_syscall_desc* stable = einfo->m_syscall_info_table; string stype(str, len); for(uint32_t j = 0; j < PPM_EVENT_MAX; j++) { if(stype == etable[j].name) { return; } } for(uint32_t j = 0; j < PPM_SC_MAX; j++) { if(stype == stable[j].name) { return; } } throw sinsp_exception("unknown event type " + stype); } else if(m_field_id == TYPE_AROUND) { if(m_cmpop != CO_EQ) { throw sinsp_exception("evt.around supports only '=' comparison operator"); } m_tsdelta = sinsp_numparser::parseu64(str) * 1000000; return; } } const filtercheck_field_info* sinsp_filter_check_event::get_field_info() { if(m_field_id == TYPE_ARGRAW) { return &m_customfield; } else { return &m_info.m_fields[m_field_id]; } } uint8_t* extract_argraw(sinsp_evt *evt, OUT uint32_t* len, const char *argname) { const sinsp_evt_param* pi = evt->get_param_value_raw(argname); if(pi != NULL) { *len = pi->m_len; return (uint8_t*)pi->m_val; } else { return NULL; } } uint8_t *sinsp_filter_check_event::extract_abspath(sinsp_evt *evt, OUT uint32_t *len) { sinsp_evt_param *parinfo; char *path; uint32_t pathlen; string spath; if(evt->m_tinfo == NULL) { return NULL; } uint16_t etype = evt->get_type(); const char *dirfdarg = NULL, *patharg = NULL; if(etype == PPME_SYSCALL_RENAMEAT_X) { if(m_argid == 0 || m_argid == 1) { dirfdarg = "olddirfd"; patharg = "oldpath"; } else if(m_argid == 2) { dirfdarg = "newdirfd"; patharg = "newpath"; } } else if(etype == PPME_SYSCALL_SYMLINKAT_X) { dirfdarg = "linkdirfd"; patharg = "linkpath"; } else if(etype == PPME_SYSCALL_OPENAT_E || etype == PPME_SYSCALL_OPENAT_2_X) { dirfdarg = "dirfd"; patharg = "name"; } else if(etype == PPME_SYSCALL_LINKAT_E || etype == PPME_SYSCALL_LINKAT_2_X) { if(m_argid == 0 || m_argid == 1) { dirfdarg = "olddir"; patharg = "oldpath"; } else if(m_argid == 2) { dirfdarg = "newdir"; patharg = "newpath"; } } else if(etype == PPME_SYSCALL_UNLINKAT_E || etype == PPME_SYSCALL_UNLINKAT_2_X) { dirfdarg = "dirfd"; patharg = "name"; } else if(etype == PPME_SYSCALL_MKDIRAT_X) { dirfdarg = "dirfd"; patharg = "path"; } else if(etype == PPME_SYSCALL_FCHMODAT_X) { dirfdarg = "dirfd"; patharg = "filename"; } if(!dirfdarg || !patharg) { return 0; } int dirfdargidx = -1, pathargidx = -1, idx = 0; while (((dirfdargidx < 0) || (pathargidx < 0)) && (idx < (int) evt->get_num_params())) { const char *name = evt->get_param_name(idx); if((dirfdargidx < 0) && (strcmp(name, dirfdarg) == 0)) { dirfdargidx = idx; } if((pathargidx < 0) && (strcmp(name, patharg) == 0)) { pathargidx = idx; } idx++; } if((dirfdargidx < 0) || (pathargidx < 0)) { return 0; } parinfo = evt->get_param(dirfdargidx); ASSERT(parinfo->m_len == sizeof(int64_t)); int64_t dirfd = *(int64_t *)parinfo->m_val; parinfo = evt->get_param(pathargidx); path = parinfo->m_val; pathlen = parinfo->m_len; string sdir; bool is_absolute = (path[0] == '/'); if(is_absolute) { // // The path is absolute. // Some processes (e.g. irqbalance) actually do this: they pass an invalid fd and // and absolute path, and openat succeeds. // sdir = "."; } else if(dirfd == PPM_AT_FDCWD) { sdir = evt->m_tinfo->get_cwd(); } else { evt->m_fdinfo = evt->m_tinfo->get_fd(dirfd); if(evt->m_fdinfo == NULL) { ASSERT(false); sdir = "/"; } else { if(evt->m_fdinfo->m_name[evt->m_fdinfo->m_name.length()] == '/') { sdir = evt->m_fdinfo->m_name; } else { sdir = evt->m_fdinfo->m_name + '/'; } } } char fullname[SCAP_MAX_PATH_SIZE]; sinsp_utils::concatenate_paths(fullname, SCAP_MAX_PATH_SIZE, sdir.c_str(), (uint32_t)sdir.length(), path, pathlen); m_strstorage = fullname; RETURN_EXTRACT_STRING(m_strstorage); } inline uint8_t* sinsp_filter_check_event::extract_buflen(sinsp_evt *evt, OUT uint32_t* len) { if(evt->get_direction() == SCAP_ED_OUT) { sinsp_evt_param *parinfo; int64_t retval; // // Extract the return value // parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); retval = *(int64_t *)parinfo->m_val; if(retval >= 0) { RETURN_EXTRACT_PTR(parinfo->m_val); } } return NULL; } Json::Value sinsp_filter_check_event::extract_as_js(sinsp_evt *evt, OUT uint32_t* len) { switch(m_field_id) { case TYPE_TIME: case TYPE_TIME_S: case TYPE_TIME_ISO8601: case TYPE_DATETIME: case TYPE_RUNTIME_TIME_OUTPUT_FORMAT: return (Json::Value::Int64)evt->get_ts(); case TYPE_RAWTS: case TYPE_RAWTS_S: case TYPE_RAWTS_NS: case TYPE_RELTS: case TYPE_RELTS_S: case TYPE_RELTS_NS: case TYPE_LATENCY: case TYPE_LATENCY_S: case TYPE_LATENCY_NS: case TYPE_DELTA: case TYPE_DELTA_S: case TYPE_DELTA_NS: return (Json::Value::Int64)*(uint64_t*)extract(evt, len); case TYPE_COUNT: m_u32val = 1; return m_u32val; default: return Json::nullValue; } return Json::nullValue; } uint8_t* sinsp_filter_check_event::extract_error_count(sinsp_evt *evt, OUT uint32_t* len) { const sinsp_evt_param* pi = evt->get_param_value_raw("res"); if(pi != NULL) { ASSERT(pi->m_len == sizeof(uint64_t)); int64_t res = *(int64_t*)pi->m_val; if(res < 0) { m_u32val = 1; RETURN_EXTRACT_VAR(m_u32val); } else { return NULL; } } if((evt->get_info_flags() & EF_CREATES_FD) && PPME_IS_EXIT(evt->get_type())) { pi = evt->get_param_value_raw("fd"); if(pi != NULL) { ASSERT(pi->m_len == sizeof(uint64_t)); int64_t res = *(int64_t*)pi->m_val; if(res < 0) { m_u32val = 1; RETURN_EXTRACT_VAR(m_u32val); } } } return NULL; } uint8_t* sinsp_filter_check_event::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = 0; switch(m_field_id) { case TYPE_TIME: // if(g_filterchecks_force_raw_times) if(false) { m_strstorage = to_string(evt->get_ts()); } else { sinsp_utils::ts_to_string(evt->get_ts(), &m_strstorage, false, true); } RETURN_EXTRACT_STRING(m_strstorage); case TYPE_TIME_S: sinsp_utils::ts_to_string(evt->get_ts(), &m_strstorage, false, false); RETURN_EXTRACT_STRING(m_strstorage); case TYPE_TIME_ISO8601: sinsp_utils::ts_to_iso_8601(evt->get_ts(), &m_strstorage); RETURN_EXTRACT_STRING(m_strstorage); case TYPE_DATETIME: sinsp_utils::ts_to_string(evt->get_ts(), &m_strstorage, true, true); RETURN_EXTRACT_STRING(m_strstorage); case TYPE_RAWTS: RETURN_EXTRACT_VAR(evt->m_pevt->ts); case TYPE_RAWTS_S: m_u64val = evt->get_ts() / ONE_SECOND_IN_NS; RETURN_EXTRACT_VAR(m_u64val); case TYPE_RAWTS_NS: m_u64val = evt->get_ts() % ONE_SECOND_IN_NS; RETURN_EXTRACT_VAR(m_u64val); case TYPE_RELTS: m_u64val = evt->get_ts() - m_inspector->m_firstevent_ts; RETURN_EXTRACT_VAR(m_u64val); case TYPE_RELTS_S: m_u64val = (evt->get_ts() - m_inspector->m_firstevent_ts) / ONE_SECOND_IN_NS; RETURN_EXTRACT_VAR(m_u64val); case TYPE_RELTS_NS: m_u64val = (evt->get_ts() - m_inspector->m_firstevent_ts) % ONE_SECOND_IN_NS; RETURN_EXTRACT_VAR(m_u64val); case TYPE_LATENCY: { m_u64val = 0; if(evt->m_tinfo != NULL) { ppm_event_category ecat = evt->get_info_category(); if(ecat & EC_INTERNAL) { return NULL; } m_u64val = evt->m_tinfo->m_latency; } RETURN_EXTRACT_VAR(m_u64val); } case TYPE_LATENCY_HUMAN: { m_u64val = 0; if(evt->m_tinfo != NULL) { ppm_event_category ecat = evt->get_info_category(); if(ecat & EC_INTERNAL) { return NULL; } m_converter->set_val(PT_RELTIME, (uint8_t*)&evt->m_tinfo->m_latency, 8, 0, ppm_print_format::PF_DEC); m_strstorage = m_converter->tostring_nice(NULL, 0, 1000000000); } RETURN_EXTRACT_STRING(m_strstorage); } case TYPE_LATENCY_S: case TYPE_LATENCY_NS: { m_u64val = 0; if(evt->m_tinfo != NULL) { ppm_event_category ecat = evt->get_info_category(); if(ecat & EC_INTERNAL) { return NULL; } uint64_t lat = evt->m_tinfo->m_latency; if(m_field_id == TYPE_LATENCY_S) { m_u64val = lat / 1000000000; } else { m_u64val = lat % 1000000000; } } RETURN_EXTRACT_VAR(m_u64val); } case TYPE_LATENCY_QUANTIZED: { if(evt->m_tinfo != NULL) { ppm_event_category ecat = evt->get_info_category(); if(ecat & EC_INTERNAL) { return NULL; } uint64_t lat = evt->m_tinfo->m_latency; if(lat != 0) { double llatency = log10((double)lat); if(llatency > 11) { llatency = 11; } m_u64val = (uint64_t)(llatency * g_csysdig_screen_w / 11) + 1; RETURN_EXTRACT_VAR(m_u64val); } } return NULL; } case TYPE_DELTA: case TYPE_DELTA_S: case TYPE_DELTA_NS: { if(m_u64val == 0) { m_u64val = evt->get_ts(); m_tsdelta = 0; } else { uint64_t tts = evt->get_ts(); if(m_field_id == TYPE_DELTA) { m_tsdelta = tts - m_u64val; } else if(m_field_id == TYPE_DELTA_S) { m_tsdelta = (tts - m_u64val) / ONE_SECOND_IN_NS; } else if(m_field_id == TYPE_DELTA_NS) { m_tsdelta = (tts - m_u64val) % ONE_SECOND_IN_NS; } m_u64val = tts; } RETURN_EXTRACT_VAR(m_tsdelta); } case TYPE_RUNTIME_TIME_OUTPUT_FORMAT: { char timebuffer[100]; m_strstorage = ""; switch(m_inspector->m_output_time_flag) { case 'h': sinsp_utils::ts_to_string(evt->get_ts(), &m_strstorage, false, true); RETURN_EXTRACT_STRING(m_strstorage); case 'a': m_strstorage += to_string(evt->get_ts() / ONE_SECOND_IN_NS); m_strstorage += "."; m_strstorage += to_string(evt->get_ts() % ONE_SECOND_IN_NS); RETURN_EXTRACT_STRING(m_strstorage); case 'r': m_strstorage += to_string((evt->get_ts() - m_inspector->m_firstevent_ts) / ONE_SECOND_IN_NS); m_strstorage += "."; snprintf(timebuffer, sizeof(timebuffer), "%09llu", (evt->get_ts() - m_inspector->m_firstevent_ts) % ONE_SECOND_IN_NS); m_strstorage += string(timebuffer); RETURN_EXTRACT_STRING(m_strstorage); case 'd': { if(evt->m_tinfo != NULL) { long long unsigned lat = evt->m_tinfo->m_latency; m_strstorage += to_string(lat / 1000000000); m_strstorage += "."; snprintf(timebuffer, sizeof(timebuffer), "%09llu", lat % 1000000000); m_strstorage += string(timebuffer); } else { m_strstorage = "0.000000000"; } RETURN_EXTRACT_STRING(m_strstorage); } case 'D': if(m_u64val == 0) { m_u64val = evt->get_ts(); m_tsdelta = 0; } uint64_t tts = evt->get_ts(); m_strstorage += to_string((tts - m_u64val) / ONE_SECOND_IN_NS); m_tsdelta = (tts - m_u64val) / ONE_SECOND_IN_NS; m_strstorage += "."; snprintf(timebuffer, sizeof(timebuffer), "%09llu", (tts - m_u64val) % ONE_SECOND_IN_NS); m_strstorage += string(timebuffer); m_tsdelta = (tts - m_u64val) % ONE_SECOND_IN_NS; m_u64val = tts; RETURN_EXTRACT_STRING(m_strstorage); } } case TYPE_DIR: if(PPME_IS_ENTER(evt->get_type())) { RETURN_EXTRACT_CSTR(">"); } else { RETURN_EXTRACT_CSTR("<"); } case TYPE_TYPE: { uint8_t* evname; uint16_t etype = evt->m_pevt->type; if(etype == PPME_GENERIC_E || etype == PPME_GENERIC_X) { sinsp_evt_param *parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(uint16_t)); uint16_t evid = *(uint16_t *)parinfo->m_val; evname = (uint8_t*)g_infotables.m_syscall_info_table[evid].name; } else { evname = (uint8_t*)evt->get_name(); } RETURN_EXTRACT_CSTR(evname); } break; case TYPE_TYPE_IS: { uint16_t etype = evt->m_pevt->type; if(etype == m_evtid || etype == m_evtid1) { m_u32val = 1; } else { m_u32val = 0; } RETURN_EXTRACT_VAR(m_u32val); } break; case TYPE_SYSCALL_TYPE: { uint8_t* evname; uint16_t etype = evt->m_pevt->type; enum ppm_event_flags flags = g_infotables.m_event_info[etype].flags; if(etype == PPME_SCHEDSWITCH_6_E || (flags & EC_INTERNAL) || (flags & EF_SKIPPARSERESET)) { return NULL; } if(etype == PPME_GENERIC_E || etype == PPME_GENERIC_X) { sinsp_evt_param *parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(uint16_t)); uint16_t evid = *(uint16_t *)parinfo->m_val; evname = (uint8_t*)g_infotables.m_syscall_info_table[evid].name; } else { evname = (uint8_t*)evt->get_name(); } RETURN_EXTRACT_CSTR(evname); } break; case TYPE_CATEGORY: sinsp_evt::category cat; evt->get_category(&cat); switch(cat.m_category) { case EC_UNKNOWN: m_strstorage = "unknown"; break; case EC_OTHER: m_strstorage = "other"; break; case EC_FILE: m_strstorage = "file"; break; case EC_NET: m_strstorage = "net"; break; case EC_IPC: m_strstorage = "IPC"; break; case EC_MEMORY: m_strstorage = "memory"; break; case EC_PROCESS: m_strstorage = "process"; break; case EC_SLEEP: m_strstorage = "sleep"; break; case EC_SYSTEM: m_strstorage = "system"; break; case EC_SIGNAL: m_strstorage = "signal"; break; case EC_USER: m_strstorage = "user"; break; case EC_TIME: m_strstorage = "time"; break; case EC_PROCESSING: m_strstorage = "processing"; break; case EC_IO_READ: case EC_IO_WRITE: case EC_IO_OTHER: { switch(cat.m_subcategory) { case sinsp_evt::SC_FILE: m_strstorage = "file"; break; case sinsp_evt::SC_NET: m_strstorage = "net"; break; case sinsp_evt::SC_IPC: m_strstorage = "ipc"; break; case sinsp_evt::SC_NONE: case sinsp_evt::SC_UNKNOWN: case sinsp_evt::SC_OTHER: m_strstorage = "unknown"; break; default: ASSERT(false); m_strstorage = "unknown"; break; } } break; case EC_WAIT: m_strstorage = "wait"; break; case EC_SCHEDULER: m_strstorage = "scheduler"; break; default: m_strstorage = "unknown"; break; } RETURN_EXTRACT_STRING(m_strstorage); case TYPE_NUMBER: RETURN_EXTRACT_VAR(evt->m_evtnum); case TYPE_CPU: RETURN_EXTRACT_VAR(evt->m_cpuid); case TYPE_ARGRAW: return extract_argraw(evt, len, m_arginfo->name); break; case TYPE_ARGSTR: { const char* resolved_argstr; const char* argstr; ASSERT(m_inspector != NULL); if(m_argid != -1) { if(m_argid >= (int32_t)evt->get_num_params()) { return NULL; } argstr = evt->get_param_as_str(m_argid, &resolved_argstr, m_inspector->get_buffer_format()); } else { argstr = evt->get_param_value_str(m_argname.c_str(), &resolved_argstr, m_inspector->get_buffer_format()); } if(resolved_argstr != NULL && resolved_argstr[0] != 0) { RETURN_EXTRACT_CSTR(resolved_argstr); } else { RETURN_EXTRACT_CSTR(argstr); } } break; case TYPE_INFO: { sinsp_fdinfo_t* fdinfo = evt->m_fdinfo; if(fdinfo != NULL && fdinfo->m_callbaks != NULL) { char* il; vector* cbacks = &(fdinfo->m_callbaks->m_write_callbacks); for(auto it = cbacks->begin(); it != cbacks->end(); ++it) { if((*it)->get_info_line(&il)) { RETURN_EXTRACT_CSTR(il); } } } } // // NOTE: this falls through to TYPE_ARGSTR, and that's what we want! // Please don't add anything here! // case TYPE_ARGS: { if(evt->get_type() == PPME_GENERIC_E || evt->get_type() == PPME_GENERIC_X) { // // Don't print the arguments for generic events: they have only internal use // RETURN_EXTRACT_CSTR(""); } const char* resolved_argstr = NULL; const char* argstr = NULL; uint32_t nargs = evt->get_num_params(); m_strstorage.clear(); for(uint32_t j = 0; j < nargs; j++) { ASSERT(m_inspector != NULL); argstr = evt->get_param_as_str(j, &resolved_argstr, m_inspector->get_buffer_format()); if(resolved_argstr[0] == 0) { m_strstorage += evt->get_param_name(j); m_strstorage += '='; m_strstorage += argstr; m_strstorage += " "; } else { m_strstorage += evt->get_param_name(j); m_strstorage += '='; m_strstorage += argstr; m_strstorage += string("(") + resolved_argstr + ") "; } } RETURN_EXTRACT_STRING(m_strstorage); } break; case TYPE_BUFFER: { if(m_is_compare) { return extract_argraw(evt, len, "data"); } const char* resolved_argstr; const char* argstr; argstr = evt->get_param_value_str("data", &resolved_argstr, m_inspector->get_buffer_format()); *len = evt->m_rawbuf_str_len; return (uint8_t*)argstr; } case TYPE_BUFLEN: if(evt->m_fdinfo && evt->get_category() & EC_IO_BASE) { return extract_buflen(evt, len); } break; case TYPE_RESRAW: { const sinsp_evt_param* pi = evt->get_param_value_raw("res"); if(pi != NULL) { *len = pi->m_len; return (uint8_t*)pi->m_val; } if((evt->get_info_flags() & EF_CREATES_FD) && PPME_IS_EXIT(evt->get_type())) { pi = evt->get_param_value_raw("fd"); if(pi != NULL) { *len = pi->m_len; return (uint8_t*)pi->m_val; } } return NULL; } break; case TYPE_RESSTR: { const char* resolved_argstr; const char* argstr; const sinsp_evt_param* pi = evt->get_param_value_raw("res"); if(pi != NULL) { ASSERT(pi->m_len == sizeof(int64_t)); int64_t res = *(int64_t*)pi->m_val; if(res >= 0) { RETURN_EXTRACT_CSTR("SUCCESS"); } else { argstr = evt->get_param_value_str("res", &resolved_argstr); ASSERT(resolved_argstr != NULL && resolved_argstr[0] != 0); if(resolved_argstr != NULL && resolved_argstr[0] != 0) { RETURN_EXTRACT_CSTR(resolved_argstr); } else if(argstr != NULL) { RETURN_EXTRACT_CSTR(argstr); } } } else { if((evt->get_info_flags() & EF_CREATES_FD) && PPME_IS_EXIT(evt->get_type())) { pi = evt->get_param_value_raw("fd"); int64_t res = *(int64_t*)pi->m_val; if(res >= 0) { RETURN_EXTRACT_CSTR("SUCCESS"); } else { argstr = evt->get_param_value_str("fd", &resolved_argstr); ASSERT(resolved_argstr != NULL && resolved_argstr[0] != 0); if(resolved_argstr != NULL && resolved_argstr[0] != 0) { RETURN_EXTRACT_CSTR(resolved_argstr); } else if(argstr != NULL) { RETURN_EXTRACT_CSTR(argstr); } } } } return NULL; } break; case TYPE_FAILED: { m_u32val = 0; const sinsp_evt_param* pi = evt->get_param_value_raw("res"); if(pi != NULL) { ASSERT(pi->m_len == sizeof(int64_t)); if(*(int64_t*)pi->m_val < 0) { m_u32val = 1; } } else if((evt->get_info_flags() & EF_CREATES_FD) && PPME_IS_EXIT(evt->get_type())) { pi = evt->get_param_value_raw("fd"); if(pi != NULL) { ASSERT(pi->m_len == sizeof(int64_t)); if(*(int64_t*)pi->m_val < 0) { m_u32val = 1; } } } RETURN_EXTRACT_VAR(m_u32val); } break; case TYPE_ISIO: { ppm_event_flags eflags = evt->get_info_flags(); if(eflags & (EF_READS_FROM_FD | EF_WRITES_TO_FD)) { m_u32val = 1; } else { m_u32val = 0; } } RETURN_EXTRACT_VAR(m_u32val); case TYPE_ISIO_READ: { ppm_event_flags eflags = evt->get_info_flags(); if(eflags & EF_READS_FROM_FD) { m_u32val = 1; } else { m_u32val = 0; } RETURN_EXTRACT_VAR(m_u32val); } case TYPE_ISIO_WRITE: { ppm_event_flags eflags = evt->get_info_flags(); if(eflags & EF_WRITES_TO_FD) { m_u32val = 1; } else { m_u32val = 0; } RETURN_EXTRACT_VAR(m_u32val); } case TYPE_IODIR: { ppm_event_flags eflags = evt->get_info_flags(); if(eflags & EF_WRITES_TO_FD) { m_strstorage = "write"; } else if(eflags & EF_READS_FROM_FD) { m_strstorage = "read"; } else { return NULL; } RETURN_EXTRACT_STRING(m_strstorage); } case TYPE_ISWAIT: { ppm_event_flags eflags = evt->get_info_flags(); if(eflags & (EF_WAITS)) { m_u32val = 1; } else { m_u32val = 0; } } RETURN_EXTRACT_VAR(m_u32val); case TYPE_WAIT_LATENCY: { ppm_event_flags eflags = evt->get_info_flags(); uint16_t etype = evt->m_pevt->type; if(eflags & (EF_WAITS) && PPME_IS_EXIT(etype)) { if(evt->m_tinfo != NULL) { m_u64val = evt->m_tinfo->m_latency; } else { m_u64val = 0; } RETURN_EXTRACT_VAR(m_u64val); } else { return NULL; } } case TYPE_ISSYSLOG: { m_u32val = 0; ppm_event_flags eflags = evt->get_info_flags(); if(eflags & EF_WRITES_TO_FD) { sinsp_fdinfo_t* fdinfo = evt->m_fdinfo; if(fdinfo != NULL) { if(fdinfo->m_name.find("/dev/log") != string::npos) { m_u32val = 1; } } } RETURN_EXTRACT_VAR(m_u32val); } case TYPE_COUNT: m_u32val = 1; RETURN_EXTRACT_VAR(m_u32val); case TYPE_COUNT_ERROR: return extract_error_count(evt, len); case TYPE_COUNT_ERROR_FILE: { sinsp_fdinfo_t* fdinfo = evt->m_fdinfo; if(fdinfo != NULL) { if(fdinfo->m_type == SCAP_FD_FILE || fdinfo->m_type == SCAP_FD_FILE_V2 || fdinfo->m_type == SCAP_FD_DIRECTORY) { return extract_error_count(evt, len); } } else { uint16_t etype = evt->get_type(); if(etype == PPME_SYSCALL_OPEN_X || etype == PPME_SYSCALL_CREAT_X || etype == PPME_SYSCALL_OPENAT_X || etype == PPME_SYSCALL_OPENAT_2_X) { return extract_error_count(evt, len); } } return NULL; } case TYPE_COUNT_ERROR_NET: { sinsp_fdinfo_t* fdinfo = evt->m_fdinfo; if(fdinfo != NULL) { if(fdinfo->m_type == SCAP_FD_IPV4_SOCK || fdinfo->m_type == SCAP_FD_IPV6_SOCK || fdinfo->m_type == SCAP_FD_IPV4_SERVSOCK || fdinfo->m_type == SCAP_FD_IPV6_SERVSOCK || fdinfo->m_type == SCAP_FD_UNIX_SOCK) { return extract_error_count(evt, len); } } else { uint16_t etype = evt->get_type(); if(etype == PPME_SOCKET_ACCEPT_X || etype == PPME_SOCKET_ACCEPT_5_X || etype == PPME_SOCKET_ACCEPT4_X || etype == PPME_SOCKET_ACCEPT4_5_X || etype == PPME_SOCKET_CONNECT_X) { return extract_error_count(evt, len); } } return NULL; } case TYPE_COUNT_ERROR_MEMORY: { if(evt->get_category() == EC_MEMORY) { return extract_error_count(evt, len); } else { return NULL; } } case TYPE_COUNT_ERROR_OTHER: { sinsp_fdinfo_t* fdinfo = evt->m_fdinfo; if(fdinfo != NULL) { if(!(fdinfo->m_type == SCAP_FD_FILE || fdinfo->m_type == SCAP_FD_FILE_V2 || fdinfo->m_type == SCAP_FD_DIRECTORY || fdinfo->m_type == SCAP_FD_IPV4_SOCK || fdinfo->m_type == SCAP_FD_IPV6_SOCK || fdinfo->m_type == SCAP_FD_IPV4_SERVSOCK || fdinfo->m_type == SCAP_FD_IPV6_SERVSOCK || fdinfo->m_type == SCAP_FD_UNIX_SOCK)) { return extract_error_count(evt, len); } } else { uint16_t etype = evt->get_type(); if(!(etype == PPME_SYSCALL_OPEN_X || etype == PPME_SYSCALL_CREAT_X || etype == PPME_SYSCALL_OPENAT_X || etype == PPME_SYSCALL_OPENAT_2_X || etype == PPME_SOCKET_ACCEPT_X || etype == PPME_SOCKET_ACCEPT_5_X || etype == PPME_SOCKET_ACCEPT4_X || etype == PPME_SOCKET_ACCEPT4_5_X || etype == PPME_SOCKET_CONNECT_X || evt->get_category() == EC_MEMORY)) { return extract_error_count(evt, len); } } return NULL; } case TYPE_COUNT_EXIT: if(PPME_IS_EXIT(evt->get_type())) { m_u32val = 1; RETURN_EXTRACT_VAR(m_u32val); } else { return NULL; } case TYPE_COUNT_PROCINFO: { uint16_t etype = evt->get_type(); if(etype == PPME_PROCINFO_E) { sinsp_threadinfo* tinfo = evt->get_thread_info(); if(tinfo != NULL && tinfo->is_main_thread()) { m_u32val = 1; RETURN_EXTRACT_VAR(m_u32val); } } } break; case TYPE_COUNT_THREADINFO: { uint16_t etype = evt->get_type(); if(etype == PPME_PROCINFO_E) { m_u32val = 1; RETURN_EXTRACT_VAR(m_u32val); } } break; case TYPE_ABSPATH: return extract_abspath(evt, len); case TYPE_BUFLEN_IN: if(evt->m_fdinfo && evt->get_category() == EC_IO_READ) { return extract_buflen(evt, len); } break; case TYPE_BUFLEN_OUT: if(evt->m_fdinfo && evt->get_category() == EC_IO_WRITE) { return extract_buflen(evt, len); } break; case TYPE_BUFLEN_FILE: if(evt->m_fdinfo && evt->get_category() & EC_IO_BASE) { if(evt->m_fdinfo->m_type == SCAP_FD_FILE || evt->m_fdinfo->m_type == SCAP_FD_FILE_V2) { return extract_buflen(evt, len); } } break; case TYPE_BUFLEN_FILE_IN: if(evt->m_fdinfo && evt->get_category() == EC_IO_READ) { if(evt->m_fdinfo->m_type == SCAP_FD_FILE || evt->m_fdinfo->m_type == SCAP_FD_FILE_V2) { return extract_buflen(evt, len); } } break; case TYPE_BUFLEN_FILE_OUT: if(evt->m_fdinfo && evt->get_category() == EC_IO_WRITE) { if(evt->m_fdinfo->m_type == SCAP_FD_FILE || evt->m_fdinfo->m_type == SCAP_FD_FILE_V2) { return extract_buflen(evt, len); } } break; case TYPE_BUFLEN_NET: if(evt->m_fdinfo && evt->get_category() & EC_IO_BASE) { scap_fd_type etype = evt->m_fdinfo->m_type; if(etype >= SCAP_FD_IPV4_SOCK && etype <= SCAP_FD_IPV6_SERVSOCK) { return extract_buflen(evt, len); } } break; case TYPE_BUFLEN_NET_IN: if(evt->m_fdinfo && evt->get_category() == EC_IO_READ) { scap_fd_type etype = evt->m_fdinfo->m_type; if(etype >= SCAP_FD_IPV4_SOCK && etype <= SCAP_FD_IPV6_SERVSOCK) { return extract_buflen(evt, len); } } break; case TYPE_BUFLEN_NET_OUT: if(evt->m_fdinfo && evt->get_category() == EC_IO_WRITE) { scap_fd_type etype = evt->m_fdinfo->m_type; if(etype >= SCAP_FD_IPV4_SOCK && etype <= SCAP_FD_IPV6_SERVSOCK) { return extract_buflen(evt, len); } } break; case TYPE_ISOPEN_READ: case TYPE_ISOPEN_WRITE: { uint16_t etype = evt->get_type(); m_u32val = 0; if(etype == PPME_SYSCALL_OPEN_X || etype == PPME_SYSCALL_OPENAT_E || etype == PPME_SYSCALL_OPENAT_2_X) { sinsp_evt_param *parinfo; // For both OPEN_X and OPENAT_E, // flags is the 3rd argument. parinfo = evt->get_param(etype == PPME_SYSCALL_OPENAT_2_X ? 3 : 2); ASSERT(parinfo->m_len == sizeof(uint32_t)); uint32_t flags = *(uint32_t *)parinfo->m_val; // PPM open flags use 0x11 for // PPM_O_RDWR, so there's no need to // check that value explicitly. if(m_field_id == TYPE_ISOPEN_READ && flags & PPM_O_RDONLY) { m_u32val = 1; } if(m_field_id == TYPE_ISOPEN_WRITE && flags & PPM_O_WRONLY) { m_u32val = 1; } } RETURN_EXTRACT_VAR(m_u32val); } break; case TYPE_INFRA_DOCKER_NAME: case TYPE_INFRA_DOCKER_CONTAINER_ID: case TYPE_INFRA_DOCKER_CONTAINER_NAME: case TYPE_INFRA_DOCKER_CONTAINER_IMAGE: { uint16_t etype = evt->m_pevt->type; if(etype == PPME_INFRASTRUCTURE_EVENT_E) { sinsp_evt_param* parinfo = evt->get_param(2); char* descstr = (char*)parinfo->m_val; vector elements = sinsp_split(descstr, ';'); for(string ute : elements) { string e = trim(ute); if(m_field_id == TYPE_INFRA_DOCKER_NAME) { if(e.substr(0, sizeof("Event") - 1) == "Event") { vector subelements = sinsp_split(e, ':'); ASSERT(subelements.size() == 2); m_strstorage = trim(subelements[1]); RETURN_EXTRACT_STRING(m_strstorage); } } else if(m_field_id == TYPE_INFRA_DOCKER_CONTAINER_ID) { if(e.substr(0, sizeof("ID") - 1) == "ID") { vector subelements = sinsp_split(e, ':'); ASSERT(subelements.size() == 2); m_strstorage = trim(subelements[1]); if(m_strstorage.length() > 12) { m_strstorage = m_strstorage.substr(0, 12); } RETURN_EXTRACT_STRING(m_strstorage); } } else if(m_field_id == TYPE_INFRA_DOCKER_CONTAINER_NAME) { if(e.substr(0, sizeof("name") - 1) == "name") { vector subelements = sinsp_split(e, ':'); ASSERT(subelements.size() == 2); m_strstorage = trim(subelements[1]); RETURN_EXTRACT_STRING(m_strstorage); } } else if(m_field_id == TYPE_INFRA_DOCKER_CONTAINER_IMAGE) { if(e.substr(0, sizeof("Image") - 1) == "Image") { vector subelements = sinsp_split(e, ':'); ASSERT(subelements.size() == 2); m_strstorage = subelements[1]; if(m_strstorage.find("@") != string::npos) { m_strstorage = m_strstorage.substr(0, m_strstorage.find("@")); } else if(m_strstorage.find("sha256") != string::npos) { m_strstorage = e.substr(e.find(":") + 1); } m_strstorage = trim(m_strstorage); RETURN_EXTRACT_STRING(m_strstorage); } } } } } break; default: ASSERT(false); return NULL; } return NULL; } bool sinsp_filter_check_event::compare(sinsp_evt *evt) { bool res; m_is_compare = true; if(m_field_id == TYPE_ARGRAW) { uint32_t len; bool sanitize_strings = false; uint8_t* extracted_val = extract(evt, &len, sanitize_strings); if(extracted_val == NULL) { return false; } ASSERT(m_arginfo != NULL); res = flt_compare(m_cmpop, m_arginfo->type, extracted_val); } else if(m_field_id == TYPE_AROUND) { uint64_t ts = evt->get_ts(); uint64_t t1 = ts - m_tsdelta; uint64_t t2 = ts + m_tsdelta; bool res1 = ::flt_compare(CO_GE, PT_UINT64, &m_u64val, &t1); bool res2 = ::flt_compare(CO_LE, PT_UINT64, &m_u64val, &t2); return res1 && res2; } else { res = sinsp_filter_check::compare(evt); } m_is_compare = false; return res; } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_user implementation /////////////////////////////////////////////////////////////////////////////// const filtercheck_field_info sinsp_filter_check_user_fields[] = { {PT_UINT32, EPF_NONE, PF_ID, "user.uid", "user ID."}, {PT_CHARBUF, EPF_NONE, PF_NA, "user.name", "user name."}, {PT_CHARBUF, EPF_NONE, PF_NA, "user.homedir", "home directory of the user."}, {PT_CHARBUF, EPF_NONE, PF_NA, "user.shell", "user's shell."}, {PT_INT32, EPF_NONE, PF_ID, "user.loginuid", "audit user id (auid)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "user.loginname", "audit user name (auid)."}, }; sinsp_filter_check_user::sinsp_filter_check_user() { m_info.m_name = "user"; m_info.m_fields = sinsp_filter_check_user_fields; m_info.m_nfields = sizeof(sinsp_filter_check_user_fields) / sizeof(sinsp_filter_check_user_fields[0]); m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; } sinsp_filter_check* sinsp_filter_check_user::allocate_new() { return (sinsp_filter_check*) new sinsp_filter_check_user(); } uint8_t* sinsp_filter_check_user::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = 0; sinsp_threadinfo* tinfo = evt->get_thread_info(); scap_userinfo* uinfo = nullptr; if(tinfo == NULL) { return NULL; } if(m_field_id != TYPE_UID && m_field_id != TYPE_LOGINUID && m_field_id != TYPE_LOGINNAME) { ASSERT(m_inspector != NULL); uinfo = m_inspector->get_user(tinfo->m_uid); ASSERT(uinfo != NULL); if(uinfo == NULL) { return NULL; } } switch(m_field_id) { case TYPE_UID: RETURN_EXTRACT_VAR(tinfo->m_uid); case TYPE_NAME: RETURN_EXTRACT_CSTR(uinfo->name); case TYPE_HOMEDIR: RETURN_EXTRACT_CSTR(uinfo->homedir); case TYPE_SHELL: RETURN_EXTRACT_CSTR(uinfo->shell); case TYPE_LOGINUID: RETURN_EXTRACT_VAR(tinfo->m_loginuid); case TYPE_LOGINNAME: ASSERT(m_inspector != NULL); uinfo = m_inspector->get_user(tinfo->m_loginuid); if(uinfo == NULL) { return NULL; } RETURN_EXTRACT_CSTR(uinfo->name); default: ASSERT(false); break; } return NULL; } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_group implementation /////////////////////////////////////////////////////////////////////////////// const filtercheck_field_info sinsp_filter_check_group_fields[] = { {PT_UINT64, EPF_NONE, PF_ID, "group.gid", "group ID."}, {PT_CHARBUF, EPF_NONE, PF_NA, "group.name", "group name."}, }; sinsp_filter_check_group::sinsp_filter_check_group() { m_info.m_name = "group"; m_info.m_fields = sinsp_filter_check_group_fields; m_info.m_nfields = sizeof(sinsp_filter_check_group_fields) / sizeof(sinsp_filter_check_group_fields[0]); m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; } sinsp_filter_check* sinsp_filter_check_group::allocate_new() { return (sinsp_filter_check*) new sinsp_filter_check_group(); } uint8_t* sinsp_filter_check_group::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = 0; sinsp_threadinfo* tinfo = evt->get_thread_info(); if(tinfo == NULL) { return NULL; } switch(m_field_id) { case TYPE_GID: RETURN_EXTRACT_VAR(tinfo->m_gid); case TYPE_NAME: { unordered_map::iterator it; ASSERT(m_inspector != NULL); unordered_map* grouplist = (unordered_map*)m_inspector->get_grouplist(); ASSERT(grouplist->size() != 0); if(tinfo->m_gid == 0xffffffff) { return NULL; } it = grouplist->find(tinfo->m_gid); if(it == grouplist->end()) { ASSERT(false); return NULL; } scap_groupinfo* ginfo = it->second; ASSERT(ginfo != NULL); RETURN_EXTRACT_CSTR(ginfo->name); } default: ASSERT(false); break; } return NULL; } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_tracer implementation /////////////////////////////////////////////////////////////////////////////// const filtercheck_field_info sinsp_filter_check_tracer_fields[] = { {PT_INT64, EPF_NONE, PF_ID, "span.id", "ID of the span. This is a unique identifier that is used to match the enter and exit tracer events for this span. It can also be used to match different spans belonging to a trace."}, {PT_CHARBUF, EPF_NONE, PF_NA, "span.time", "time of the span's enter tracer as a human readable string that includes the nanosecond part."}, {PT_UINT32, EPF_NONE, PF_DEC, "span.ntags", "number of tags that this span has."}, {PT_UINT32, EPF_NONE, PF_DEC, "span.nargs", "number of arguments that this span has."}, {PT_CHARBUF, EPF_NONE, PF_NA, "span.tags", "dot-separated list of all of the span's tags."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "span.tag", "one of the span's tags, specified by 0-based offset, e.g. 'span.tag[1]'. You can use a negative offset to pick elements from the end of the tag list. For example, 'span.tag[-1]' returns the last tag."}, {PT_CHARBUF, EPF_NONE, PF_NA, "span.args", "comma-separated list of the span's arguments." }, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "span.arg", "one of the span arguments, specified by name or by 0-based offset. E.g. 'span.arg.xxx' or 'span.arg[1]'. You can use a negative offset to pick elements from the end of the tag list. For example, 'span.arg[-1]' returns the last argument." }, {PT_CHARBUF, EPF_NONE, PF_NA, "span.enterargs", "comma-separated list of the span's enter tracer event arguments. For enter tracers, this is the same as evt.args. For exit tracers, this is the evt.args of the corresponding enter tracer." }, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "span.enterarg", "one of the span's enter arguments, specified by name or by 0-based offset. For enter tracer events, this is the same as evt.arg. For exit tracer events, this is the evt.arg of the corresponding enter event." }, {PT_RELTIME, EPF_NONE, PF_DEC, "span.duration", "delta between this span's exit tracer event and the enter tracer event."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "span.duration.quantized", "10-base log of the delta between an exit tracer event and the correspondent enter event."}, {PT_CHARBUF, EPF_NONE, PF_NA, "span.duration.human", "delta between this span's exit tracer event and the enter event, as a human readable string (e.g. 10.3ms)."}, {PT_RELTIME, (filtercheck_field_flags) (EPF_TABLE_ONLY | EPF_REQUIRES_ARGUMENT), PF_DEC, "span.duration.fortag", "duration of the span if the number of tags matches the field argument, otherwise 0. For example, span.duration.fortag[1] returns the duration of all the spans with 1 tag, and zero for all the other ones."}, {PT_UINT64, EPF_TABLE_ONLY, PF_DEC, "span.count", "1 for span exit events."}, {PT_UINT64, (filtercheck_field_flags) (EPF_TABLE_ONLY | EPF_REQUIRES_ARGUMENT), PF_DEC, "span.count.fortag", "1 if the span's number of tags matches the field argument, and zero for all the other ones."}, {PT_UINT64, (filtercheck_field_flags) (EPF_TABLE_ONLY | EPF_REQUIRES_ARGUMENT), PF_DEC, "span.childcount.fortag", "1 if the span's number of tags is greater than the field argument, and zero for all the other ones."}, {PT_CHARBUF, (filtercheck_field_flags) (EPF_TABLE_ONLY | EPF_REQUIRES_ARGUMENT), PF_NA, "span.idtag", "id used by the span list csysdig view."}, {PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "span.rawtime", "id used by the span list csysdig view."}, {PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "span.rawparenttime", "id used by the span list csysdig view."}, }; sinsp_filter_check_tracer::sinsp_filter_check_tracer() { m_storage = NULL; m_info.m_name = "span"; m_info.m_fields = sinsp_filter_check_tracer_fields; m_info.m_nfields = sizeof(sinsp_filter_check_tracer_fields) / sizeof(sinsp_filter_check_tracer_fields[0]); m_converter = new sinsp_filter_check_reference(); m_storage_size = UESTORAGE_INITIAL_BUFSIZE; m_storage = (char*)malloc(m_storage_size); if(m_storage == NULL) { throw sinsp_exception("memory allocation error in sinsp_filter_check_tracer::sinsp_filter_check_tracer"); } m_cargname = NULL; } sinsp_filter_check_tracer::~sinsp_filter_check_tracer() { if(m_converter != NULL) { delete m_converter; } if(m_storage != NULL) { free(m_storage); } } sinsp_filter_check* sinsp_filter_check_tracer::allocate_new() { return (sinsp_filter_check*) new sinsp_filter_check_tracer(); } int32_t sinsp_filter_check_tracer::extract_arg(string fldname, string val, OUT const struct ppm_param_info** parinfo) { uint32_t parsed_len = 0; // // 'arg' and 'resarg' are handled in a custom way // if(val[fldname.size()] == '[') { if(parinfo != NULL) { throw sinsp_exception("tracer field must be expressed explicitly"); } parsed_len = (uint32_t)val.find(']'); string numstr = val.substr(fldname.size() + 1, parsed_len - fldname.size() - 1); m_argid = sinsp_numparser::parsed32(numstr); parsed_len++; } else if(val[fldname.size()] == '.') { if(fldname == "span.tag") { throw sinsp_exception("invalid syntax for span.tag"); } else if(fldname == "span.idtag") { throw sinsp_exception("invalid syntax for span.idtag"); } m_argname = val.substr(fldname.size() + 1); m_cargname = m_argname.c_str(); parsed_len = (uint32_t)(fldname.size() + m_argname.size() + 1); m_argid = TEXT_ARG_ID; } else { throw sinsp_exception("filter syntax error: " + val); } return parsed_len; } int32_t sinsp_filter_check_tracer::parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) { int32_t res; string val(str); // // A couple of fields are handled in a custom way // if(string(val, 0, sizeof("span.tag") - 1) == "span.tag" && string(val, 0, sizeof("span.tags") - 1) != "span.tags") { m_field_id = TYPE_TAG; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("span.tag", val, NULL); } else if(string(val, 0, sizeof("span.arg") - 1) == "span.arg" && string(val, 0, sizeof("span.args") - 1) != "span.args") { m_field_id = TYPE_ARG; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("span.arg", val, NULL); } else if(string(val, 0, sizeof("span.enterarg") - 1) == "span.enterarg" && string(val, 0, sizeof("span.enterargs") - 1) != "span.enterargs") { m_field_id = TYPE_ENTERARG; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("span.enterarg", val, NULL); } else if(string(val, 0, sizeof("span.duration.fortag") - 1) == "span.duration.fortag") { m_field_id = TYPE_TAGDURATION; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("span.duration.fortag", val, NULL); } else if(string(val, 0, sizeof("span.count.fortag") - 1) == "span.count.fortag") { m_field_id = TYPE_TAGCOUNT; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("span.count.fortag", val, NULL); } else if(string(val, 0, sizeof("span.childcount.fortag") - 1) == "span.childcount.fortag") { m_field_id = TYPE_TAGCHILDSCOUNT; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("span.childcount.fortag", val, NULL); } else if(string(val, 0, sizeof("span.idtag") - 1) == "span.idtag") { m_field_id = TYPE_IDTAG; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("span.idtag", val, NULL); } else { res = sinsp_filter_check::parse_field_name(str, alloc_state, needed_for_filtering); } if(m_field_id == TYPE_DURATION || m_field_id == TYPE_DURATION_QUANTIZED || m_field_id == TYPE_DURATION_HUMAN || m_field_id == TYPE_TAGDURATION || m_field_id == TYPE_ARG || m_field_id == TYPE_ARGS || m_field_id == TYPE_ENTERARG || m_field_id == TYPE_ENTERARGS || m_field_id == TYPE_IDTAG || m_field_id == TYPE_TIME || m_field_id == TYPE_RAWTIME || m_field_id == TYPE_RAWPARENTTIME ) { m_inspector->request_tracer_state_tracking(); m_needs_state_tracking = true; } return res; } uint8_t* sinsp_filter_check_tracer::extract_duration(uint16_t etype, sinsp_tracerparser* eparser, OUT uint32_t* len) { if(etype == PPME_TRACER_X) { sinsp_partial_tracer* pae = eparser->m_enter_pae; if(pae == NULL) { return NULL; } m_s64val = eparser->m_exit_pae.m_time - pae->m_time; if(m_s64val < 0) { ASSERT(false); m_s64val = 0; } RETURN_EXTRACT_VAR(m_s64val); } else { return NULL; } } uint8_t* sinsp_filter_check_tracer::extract_args(sinsp_partial_tracer* pae, OUT uint32_t* len) { if(pae == NULL) { return NULL; } vector::iterator nameit; vector::iterator valit; vector::iterator namesit; vector::iterator valsit; uint32_t nargs = (uint32_t)pae->m_argnames.size(); uint32_t encoded_args_len = pae->m_argnames_len + pae->m_argvals_len + nargs + nargs + 2; if(m_storage_size < encoded_args_len) { m_storage = (char*)realloc(m_storage, encoded_args_len); m_storage_size = encoded_args_len; } char* p = m_storage; for(nameit = pae->m_argnames.begin(), valit = pae->m_argvals.begin(), namesit = pae->m_argnamelens.begin(), valsit = pae->m_argvallens.begin(); nameit != pae->m_argnames.end(); ++nameit, ++namesit, ++valit, ++valsit) { strcpy(p, *nameit); p += (*namesit); *p++ = '='; memcpy(p, *valit, (*valsit)); p += (*valsit); *p++ = ','; } if(p != m_storage) { *--p = 0; } else { *p = 0; } RETURN_EXTRACT_CSTR(m_storage); } uint8_t* sinsp_filter_check_tracer::extract_arg(sinsp_partial_tracer* pae, OUT uint32_t* len) { char* res = NULL; if(pae == NULL) { return NULL; } if(m_argid == TEXT_ARG_ID) { // // Argument expressed as name, e.g. span.arg.name. // Scan the argname list and find the match. // uint32_t j; for(j = 0; j < pae->m_nargs; j++) { if(strcmp(m_cargname, pae->m_argnames[j]) == 0) { res = pae->m_argvals[j]; break; } } } else { // // Argument expressed as id, e.g. span.arg[1]. // Pick the corresponding value. // if(m_argid >= 0) { if(m_argid < (int32_t)pae->m_nargs) { res = pae->m_argvals[m_argid]; } } else { int32_t id = (int32_t)pae->m_nargs + m_argid; if(id >= 0) { res = pae->m_argvals[id]; } } } if (res) { *len = strlen(res); } return (uint8_t*)res; } uint8_t* sinsp_filter_check_tracer::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = 0; sinsp_tracerparser* eparser; sinsp_threadinfo* tinfo = evt->get_thread_info(); uint16_t etype = evt->get_type(); if(etype != PPME_TRACER_E && etype != PPME_TRACER_X) { return NULL; } if(tinfo == NULL) { return NULL; } eparser = tinfo->m_tracer_parser; if(eparser == NULL) { return NULL; } else { if(m_needs_state_tracking && eparser->m_enter_pae == NULL) { return NULL; } } switch(m_field_id) { case TYPE_ID: RETURN_EXTRACT_VAR(eparser->m_id); case TYPE_TIME: { sinsp_utils::ts_to_string(evt->get_ts(), &m_strstorage, false, true); RETURN_EXTRACT_STRING(m_strstorage); } case TYPE_NTAGS: m_u32val = (uint32_t)eparser->m_tags.size(); RETURN_EXTRACT_VAR(m_u32val); case TYPE_NARGS: { sinsp_partial_tracer* pae = eparser->m_enter_pae; if(pae == NULL) { return NULL; } m_u32val = (uint32_t)pae->m_argvals.size(); RETURN_EXTRACT_VAR(m_u32val); } case TYPE_TAGS: { vector::iterator it; vector::iterator sit; uint32_t ntags = (uint32_t)eparser->m_tags.size(); uint32_t encoded_tags_len = eparser->m_tot_taglens + ntags + 1; if(m_storage_size < encoded_tags_len) { m_storage = (char*)realloc(m_storage, encoded_tags_len); m_storage_size = encoded_tags_len; } char* p = m_storage; for(it = eparser->m_tags.begin(), sit = eparser->m_taglens.begin(); it != eparser->m_tags.end(); ++it, ++sit) { memcpy(p, *it, (*sit)); p += (*sit); *p++ = '.'; } if(p != m_storage) { *--p = 0; } else { *p = 0; } RETURN_EXTRACT_CSTR(m_storage); } case TYPE_TAG: { char* res = NULL; if(m_argid >= 0) { if(m_argid < (int32_t)eparser->m_tags.size()) { res = eparser->m_tags[m_argid]; } } else { int32_t id = (int32_t)eparser->m_tags.size() + m_argid; if(id >= 0) { res = eparser->m_tags[id]; } } RETURN_EXTRACT_CSTR(res); } case TYPE_IDTAG: { m_strstorage = to_string(eparser->m_id); if(m_argid >= 0) { if(m_argid < (int32_t)eparser->m_tags.size()) { m_strstorage += eparser->m_tags[m_argid]; } } else { int32_t id = (int32_t)eparser->m_tags.size() + m_argid; if(id >= 0) { m_strstorage += eparser->m_tags[id]; } } RETURN_EXTRACT_STRING(m_strstorage); } case TYPE_ARGS: if(PPME_IS_ENTER(etype)) { return extract_args(eparser->m_enter_pae, len); } else { return extract_args(&eparser->m_exit_pae, len); } case TYPE_ARG: if(PPME_IS_ENTER(etype)) { return extract_arg(eparser->m_enter_pae, len); } else { return extract_arg(&eparser->m_exit_pae, len); } case TYPE_ENTERARGS: return extract_args(eparser->m_enter_pae, len); case TYPE_ENTERARG: return extract_arg(eparser->m_enter_pae, len); case TYPE_DURATION: return (uint8_t*)extract_duration(etype, eparser, len); case TYPE_DURATION_HUMAN: { if(extract_duration(etype, eparser, len) == NULL) { return NULL; } else { m_converter->set_val(PT_RELTIME, (uint8_t*)&m_s64val, 8, 0, ppm_print_format::PF_DEC); m_strstorage = m_converter->tostring_nice(NULL, 0, 1000000000); } RETURN_EXTRACT_STRING(m_strstorage); } case TYPE_DURATION_QUANTIZED: { if(extract_duration(etype, eparser, len) == NULL) { return NULL; } else { uint64_t lat = m_s64val; if(lat != 0) { double lduration = log10((double)lat); if(lduration > 11) { lduration = 11; } m_s64val = (uint64_t)(lduration * g_csysdig_screen_w / 11) + 1; RETURN_EXTRACT_VAR(m_s64val); } } return NULL; } case TYPE_TAGDURATION: if((int32_t)eparser->m_tags.size() - 1 == m_argid) { return (uint8_t*)extract_duration(etype, eparser, len); } else { return NULL; } case TYPE_COUNT: if(evt->get_type() == PPME_TRACER_X) { m_s64val = 1; } else { m_s64val = 0; } RETURN_EXTRACT_VAR(m_s64val); case TYPE_TAGCOUNT: if(PPME_IS_EXIT(evt->get_type()) && (int32_t)eparser->m_tags.size() - 1 == m_argid) { m_s64val = 1; } else { m_s64val = 0; } RETURN_EXTRACT_VAR(m_s64val); case TYPE_TAGCHILDSCOUNT: if(PPME_IS_EXIT(evt->get_type()) && (int32_t)eparser->m_tags.size() > m_argid + 1) { m_s64val = 1; } else { m_s64val = 0; } RETURN_EXTRACT_VAR(m_s64val); case TYPE_RAWTIME: { m_strstorage = to_string(eparser->m_enter_pae->m_time); RETURN_EXTRACT_STRING(m_strstorage); } case TYPE_RAWPARENTTIME: { sinsp_partial_tracer* pepae = eparser->find_parent_enter_pae(); if(pepae == NULL) { return NULL; } m_strstorage = to_string(pepae->m_time); RETURN_EXTRACT_STRING(m_strstorage); } default: ASSERT(false); break; } return NULL; } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_tracer implementation /////////////////////////////////////////////////////////////////////////////// const filtercheck_field_info sinsp_filter_check_evtin_fields[] = { { PT_INT64, EPF_NONE, PF_ID, "evtin.span.id", "accepts all the events that are between the enter and exit tracers of the spans with the given ID and are generated by the same thread that generated the tracers." }, { PT_UINT32, EPF_NONE, PF_DEC, "evtin.span.ntags", "accepts all the events that are between the enter and exit tracers of the spans with the given number of tags and are generated by the same thread that generated the tracers." }, { PT_UINT32, EPF_NONE, PF_DEC, "evtin.span.nargs", "accepts all the events that are between the enter and exit tracers of the spans with the given number of arguments and are generated by the same thread that generated the tracers." }, { PT_CHARBUF, EPF_NONE, PF_NA, "evtin.span.tags", "accepts all the events that are between the enter and exit tracers of the spans with the given tags and are generated by the same thread that generated the tracers." }, { PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "evtin.span.tag", "accepts all the events that are between the enter and exit tracers of the spans with the given tag and are generated by the same thread that generated the tracers. See the description of span.tag for information about the syntax accepted by this field." }, { PT_CHARBUF, EPF_NONE, PF_NA, "evtin.span.args", "accepts all the events that are between the enter and exit tracers of the spans with the given arguments and are generated by the same thread that generated the tracers." }, { PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "evtin.span.arg", "accepts all the events that are between the enter and exit tracers of the spans with the given argument and are generated by the same thread that generated the tracers. See the description of span.arg for information about the syntax accepted by this field." }, { PT_INT64, EPF_NONE, PF_ID, "evtin.span.p.id", "same as evtin.span.id, but also accepts events generated by other threads in the same process that produced the span." }, { PT_UINT32, EPF_NONE, PF_DEC, "evtin.span.p.ntags", "same as evtin.span.ntags, but also accepts events generated by other threads in the same process that produced the span." }, { PT_UINT32, EPF_NONE, PF_DEC, "evtin.span.p.nargs", "same as evtin.span.nargs, but also accepts events generated by other threads in the same process that produced the span." }, { PT_CHARBUF, EPF_NONE, PF_NA, "evtin.span.p.tags", "same as evtin.span.tags, but also accepts events generated by other threads in the same process that produced the span." }, { PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "evtin.span.p.tag", "same as evtin.span.tag, but also accepts events generated by other threads in the same process that produced the span." }, { PT_CHARBUF, EPF_NONE, PF_NA, "evtin.span.p.args", "same as evtin.span.args, but also accepts events generated by other threads in the same process that produced the span." }, { PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "evtin.span.p.arg", "same as evtin.span.arg, but also accepts events generated by other threads in the same process that produced the span." }, { PT_INT64, EPF_NONE, PF_ID, "evtin.span.s.id", "same as evtin.span.id, but also accepts events generated by the script that produced the span, i.e. by the processes whose parent PID is the same as the one of the process generating the span." }, { PT_UINT32, EPF_NONE, PF_DEC, "evtin.span.s.ntags", "same as evtin.span.id, but also accepts events generated by the script that produced the span, i.e. by the processes whose parent PID is the same as the one of the process generating the span." }, { PT_UINT32, EPF_NONE, PF_DEC, "evtin.span.s.nargs", "same as evtin.span.id, but also accepts events generated by the script that produced the span, i.e. by the processes whose parent PID is the same as the one of the process generating the span." }, { PT_CHARBUF, EPF_NONE, PF_NA, "evtin.span.s.tags", "same as evtin.span.id, but also accepts events generated by the script that produced the span, i.e. by the processes whose parent PID is the same as the one of the process generating the span." }, { PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "evtin.span.s.tag", "same as evtin.span.id, but also accepts events generated by the script that produced the span, i.e. by the processes whose parent PID is the same as the one of the process generating the span." }, { PT_CHARBUF, EPF_NONE, PF_NA, "evtin.span.s.args", "same as evtin.span.id, but also accepts events generated by the script that produced the span, i.e. by the processes whose parent PID is the same as the one of the process generating the span." }, { PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "evtin.span.s.arg", "same as evtin.span.id, but also accepts events generated by the script that produced the span, i.e. by the processes whose parent PID is the same as the one of the process generating the span." }, { PT_INT64, EPF_NONE, PF_ID, "evtin.span.m.id", "same as evtin.span.id, but accepts all the events generated on the machine during the span, including other threads and other processes." }, { PT_UINT32, EPF_NONE, PF_DEC, "evtin.span.m.ntags", "same as evtin.span.id, but accepts all the events generated on the machine during the span, including other threads and other processes." }, { PT_UINT32, EPF_NONE, PF_DEC, "evtin.span.m.nargs", "same as evtin.span.id, but accepts all the events generated on the machine during the span, including other threads and other processes." }, { PT_CHARBUF, EPF_NONE, PF_NA, "evtin.span.m.tags", "same as evtin.span.id, but accepts all the events generated on the machine during the span, including other threads and other processes." }, { PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "evtin.span.m.tag", "same as evtin.span.id, but accepts all the events generated on the machine during the span, including other threads and other processes." }, { PT_CHARBUF, EPF_NONE, PF_NA, "evtin.span.m.args", "same as evtin.span.id, but accepts all the events generated on the machine during the span, including other threads and other processes." }, { PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "evtin.span.m.arg", "same as evtin.span.id, but accepts all the events generated on the machine during the span, including other threads and other processes." }, }; sinsp_filter_check_evtin::sinsp_filter_check_evtin() { m_is_compare = false; m_info.m_name = "evtin"; m_info.m_fields = sinsp_filter_check_evtin_fields; m_info.m_nfields = sizeof(sinsp_filter_check_evtin_fields) / sizeof(sinsp_filter_check_evtin_fields[0]); m_u64val = 0; m_converter = new sinsp_filter_check_reference(); m_storage_size = UESTORAGE_INITIAL_BUFSIZE; m_storage = (char*)malloc(m_storage_size); if(m_storage == NULL) { throw sinsp_exception("memory allocation error in sinsp_filter_check_appevt::sinsp_filter_check_evtin"); } m_cargname = NULL; } sinsp_filter_check_evtin::~sinsp_filter_check_evtin() { if(m_storage != NULL) { free(m_storage); } if(m_converter != NULL) { delete m_converter; } } int32_t sinsp_filter_check_evtin::extract_arg(string fldname, string val) { uint32_t parsed_len = 0; // // 'arg' and 'resarg' are handled in a custom way // if(val[fldname.size()] == '[') { parsed_len = (uint32_t)val.find(']'); string numstr = val.substr(fldname.size() + 1, parsed_len - fldname.size() - 1); m_argid = sinsp_numparser::parsed32(numstr); parsed_len++; } else if(val[fldname.size()] == '.') { const struct ppm_param_info* pi = sinsp_utils::find_longest_matching_evt_param(val.substr(fldname.size() + 1)); if(pi == NULL) { throw sinsp_exception("unknown event argument " + val.substr(fldname.size() + 1)); } m_argname = pi->name; parsed_len = (uint32_t)(fldname.size() + strlen(pi->name) + 1); m_argid = -1; } else { throw sinsp_exception("filter syntax error: " + val); } return parsed_len; } int32_t sinsp_filter_check_evtin::parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) { int32_t res; string val(str); // // All of the fields require state tracking // m_inspector->request_tracer_state_tracking(); // // A couple of fields are handled in a custom way // if(string(val, 0, sizeof("evtin.span.tag") - 1) == "evtin.span.tag" && string(val, 0, sizeof("evtin.span.tags") - 1) != "evtin.span.tags") { m_field_id = TYPE_TAG; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("evtin.span.tag", val); } else if(string(val, 0, sizeof("evtin.span.arg") - 1) == "evtin.span.arg" && string(val, 0, sizeof("evtin.span.args") - 1) != "evtin.span.args") { m_field_id = TYPE_ARG; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("evtin.span.arg", val); } else if(string(val, 0, sizeof("evtin.span.p.tag") - 1) == "evtin.span.p.tag" && string(val, 0, sizeof("evtin.span.p.tags") - 1) != "evtin.span.p.tags") { m_field_id = TYPE_P_TAG; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("evtin.span.p.tag", val); } else if(string(val, 0, sizeof("evtin.span.p.arg") - 1) == "evtin.span.p.arg" && string(val, 0, sizeof("evtin.span.p.args") - 1) != "evtin.span.p.args") { m_field_id = TYPE_P_ARG; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("evtin.span.p.arg", val); } else if(string(val, 0, sizeof("evtin.span.s.tag") - 1) == "evtin.span.s.tag" && string(val, 0, sizeof("evtin.span.s.tags") - 1) != "evtin.span.s.tags") { m_field_id = TYPE_S_TAG; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("evtin.span.s.tag", val); } else if(string(val, 0, sizeof("evtin.span.s.arg") - 1) == "evtin.span.s.arg" && string(val, 0, sizeof("evtin.span.s.args") - 1) != "evtin.span.s.args") { m_field_id = TYPE_S_ARG; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("evtin.span.s.arg", val); } else if(string(val, 0, sizeof("evtin.span.m.tag") - 1) == "evtin.span.m.tag" && string(val, 0, sizeof("evtin.span.m.tags") - 1) != "evtin.span.m.tags") { m_field_id = TYPE_M_TAG; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("evtin.span.m.tag", val); } else if(string(val, 0, sizeof("evtin.span.m.arg") - 1) == "evtin.span.m.arg" && string(val, 0, sizeof("evtin.span.m.args") - 1) != "evtin.span.m.args") { m_field_id = TYPE_M_ARG; m_field = &m_info.m_fields[m_field_id]; res = extract_arg("evtin.span.m.arg", val); } else { res = sinsp_filter_check::parse_field_name(str, alloc_state, needed_for_filtering); } return res; } sinsp_filter_check* sinsp_filter_check_evtin::allocate_new() { return (sinsp_filter_check*) new sinsp_filter_check_evtin(); } inline uint8_t* sinsp_filter_check_evtin::extract_tracer(sinsp_evt *evt, sinsp_partial_tracer* pae, OUT uint32_t* len) { ASSERT(pae); uint32_t field_id = m_field_id; if(field_id >= TYPE_ID && field_id <= TYPE_ARG) { // // If this is a thread-related field, reject anything that doesn't come from the same thread // if(static_cast(pae->m_tid) != evt->get_thread_info()->m_tid) { return NULL; } } else if(field_id >= TYPE_P_ID && field_id <= TYPE_P_ARG) { // // If this is a *.p.* field, reject anything that doesn't come from the same process // sinsp_threadinfo* tinfo = m_inspector->get_thread(pae->m_tid); if(tinfo) { if(tinfo->m_tid != evt->get_thread_info()->m_tid) { return NULL; } } else { return NULL; } field_id -= TYPE_P_ID; } else if(field_id >= TYPE_S_ID && field_id <= TYPE_S_ARG) { // // If this is a *.p.* field, reject anything that doesn't share the same parent // sinsp_threadinfo* tinfo = m_inspector->get_thread(pae->m_tid); if(tinfo) { if(tinfo->m_pid != evt->get_thread_info()->m_ptid) { return NULL; } } else { return NULL; } field_id -= TYPE_S_ID; } else { field_id -= TYPE_M_ID; } switch(field_id) { case TYPE_ID: RETURN_EXTRACT_VAR(pae->m_id); case TYPE_NTAGS: m_u32val = (uint32_t)pae->m_tags.size(); RETURN_EXTRACT_VAR(m_u32val); case TYPE_NARGS: m_u32val = (uint32_t)pae->m_argvals.size(); RETURN_EXTRACT_VAR(m_u32val); case TYPE_TAGS: { vector::iterator it; vector::iterator sit; uint32_t encoded_tags_len = pae->m_tags_len + pae->m_ntags + 1; if(m_storage_size < encoded_tags_len) { m_storage = (char*)realloc(m_storage, encoded_tags_len); m_storage_size = encoded_tags_len; } char* p = m_storage; for(it = pae->m_tags.begin(), sit = pae->m_taglens.begin(); it != pae->m_tags.end(); ++it, ++sit) { memcpy(p, *it, (*sit)); p += (*sit); *p++ = '.'; } if(p != m_storage) { *--p = 0; } else { *p = 0; } RETURN_EXTRACT_CSTR(m_storage); } case TYPE_TAG: { char* val = NULL; if(m_argid >= 0) { if(m_argid < (int32_t)pae->m_ntags) { val = pae->m_tags[m_argid]; } } else { int32_t id = (int32_t)pae->m_ntags + m_argid; if(id >= 0) { val = pae->m_tags[id]; } } RETURN_EXTRACT_CSTR(val); } case TYPE_ARGS: { vector::iterator nameit; vector::iterator valit; vector::iterator namesit; vector::iterator valsit; uint32_t nargs = (uint32_t)pae->m_argnames.size(); uint32_t encoded_args_len = pae->m_argnames_len + pae->m_argvals_len + nargs + nargs + 2; if(m_storage_size < encoded_args_len) { m_storage = (char*)realloc(m_storage, encoded_args_len); m_storage_size = encoded_args_len; } char* p = m_storage; for(nameit = pae->m_argnames.begin(), valit = pae->m_argvals.begin(), namesit = pae->m_argnamelens.begin(), valsit = pae->m_argvallens.begin(); nameit != pae->m_argnames.end(); ++nameit, ++namesit, ++valit, ++valsit) { strcpy(p, *nameit); p += (*namesit); *p++ = ':'; memcpy(p, *valit, (*valsit)); p += (*valsit); *p++ = ','; } if(p != m_storage) { *--p = 0; } else { *p = 0; } RETURN_EXTRACT_CSTR(m_storage); } case TYPE_ARG: { char* val = NULL; if(m_argid == TEXT_ARG_ID) { // // Argument expressed as name, e.g. evtin.span.arg.name. // Scan the argname list and find the match. // uint32_t j; for(j = 0; j < pae->m_nargs; j++) { if(strcmp(m_cargname, pae->m_argnames[j]) == 0) { val = pae->m_argvals[j]; break; } } } else { // // Argument expressed as id, e.g. evtin.span.arg[1]. // Pick the corresponding value. // if(m_argid >= 0) { if(m_argid < (int32_t)pae->m_nargs) { val = pae->m_argvals[m_argid]; } } else { int32_t id = (int32_t)pae->m_nargs + m_argid; if(id >= 0) { val = pae->m_argvals[id]; } } } RETURN_EXTRACT_CSTR(val); } default: ASSERT(false); break; } return NULL; } uint8_t* sinsp_filter_check_evtin::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = 0; list* partial_tracers_list = &m_inspector->m_partial_tracers_list; list::iterator it; uint16_t etype = evt->get_type(); // // Tracer events are excluded // if(etype == PPME_TRACER_E || etype == PPME_TRACER_X) { return NULL; } // // Events without thread information are excluded // sinsp_threadinfo* tinfo = evt->get_thread_info(); if(tinfo == NULL || tinfo->m_tracer_parser == NULL) { return NULL; } // // Scan the list and see if there's a match // for(it = partial_tracers_list->begin(); it != partial_tracers_list->end(); ++it) { uint8_t* res = extract_tracer(evt, *it, len); if(res != NULL) { return res; } } return NULL; } inline bool sinsp_filter_check_evtin::compare_tracer(sinsp_evt *evt, sinsp_partial_tracer* pae) { uint32_t len; uint8_t* res = extract_tracer(evt, pae, &len); if(res == NULL) { return false; } if(flt_compare(m_cmpop, m_info.m_fields[m_field_id].m_type, res) == true) { return true; } else { return false; } } bool sinsp_filter_check_evtin::compare(sinsp_evt *evt) { bool res; m_is_compare = true; list* partial_tracers_list = &m_inspector->m_partial_tracers_list; list::iterator it; uint16_t etype = evt->get_type(); sinsp_threadinfo* tinfo = evt->get_thread_info(); if(tinfo == NULL) { res = false; goto fcec_end; } // // Scan the list and see if there's a match // for(it = partial_tracers_list->begin(); it != partial_tracers_list->end(); ++it) { if(compare_tracer(evt, *it) == true) { if(etype == PPME_TRACER_E && *it == tinfo->m_tracer_parser->m_enter_pae) { res = false; goto fcec_end; } res = true; goto fcec_end; } } // // For PPME_TRACER_X events, it's possible that the pae is already returned to the pool. // Get it from the parser. // if(etype == PPME_TRACER_X) { sinsp_tracerparser* eparser = tinfo->m_tracer_parser; if(eparser == NULL) { ASSERT(false); res = false; goto fcec_end; } if(eparser->m_enter_pae == NULL) { res = false; goto fcec_end; } if(compare_tracer(evt, eparser->m_enter_pae) == true) { res = true; goto fcec_end; } } res = false; fcec_end: m_is_compare = false; return res; } /////////////////////////////////////////////////////////////////////////////// // rawstring_check implementation /////////////////////////////////////////////////////////////////////////////// const filtercheck_field_info rawstring_check_fields[] = { {PT_CHARBUF, EPF_NONE, PF_NA, "NA", "INTERNAL."}, }; rawstring_check::rawstring_check(string text) { m_field = rawstring_check_fields; m_field_id = 0; set_text(text); } sinsp_filter_check* rawstring_check::allocate_new() { ASSERT(false); return NULL; } void rawstring_check::set_text(string text) { m_text_len = (uint32_t)text.size(); m_text = text; } int32_t rawstring_check::parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) { ASSERT(false); return -1; } uint8_t* rawstring_check::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = m_text_len; return (uint8_t*)m_text.c_str(); } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_syslog implementation /////////////////////////////////////////////////////////////////////////////// const filtercheck_field_info sinsp_filter_check_syslog_fields[] = { {PT_CHARBUF, EPF_NONE, PF_NA, "syslog.facility.str", "facility as a string."}, {PT_UINT32, EPF_NONE, PF_DEC, "syslog.facility", "facility as a number (0-23)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "syslog.severity.str", "severity as a string. Can have one of these values: emerg, alert, crit, err, warn, notice, info, debug"}, {PT_UINT32, EPF_NONE, PF_DEC, "syslog.severity", "severity as a number (0-7)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "syslog.message", "message sent to syslog."}, }; sinsp_filter_check_syslog::sinsp_filter_check_syslog() { m_info.m_name = "syslog"; m_info.m_fields = sinsp_filter_check_syslog_fields; m_info.m_nfields = sizeof(sinsp_filter_check_syslog_fields) / sizeof(sinsp_filter_check_syslog_fields[0]); m_decoder = NULL; } sinsp_filter_check* sinsp_filter_check_syslog::allocate_new() { return (sinsp_filter_check*) new sinsp_filter_check_syslog(); } int32_t sinsp_filter_check_syslog::parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) { int32_t res = sinsp_filter_check::parse_field_name(str, alloc_state, needed_for_filtering); if(res != -1) { m_decoder = (sinsp_decoder_syslog*)m_inspector->require_protodecoder("syslog"); } return res; } uint8_t* sinsp_filter_check_syslog::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = 0; const char *str; ASSERT(m_decoder != NULL); if(!m_decoder->is_data_valid()) { return NULL; } switch(m_field_id) { case TYPE_FACILITY: RETURN_EXTRACT_VAR(m_decoder->m_facility); case TYPE_FACILITY_STR: str = m_decoder->get_facility_str(); RETURN_EXTRACT_CSTR(str); case TYPE_SEVERITY: RETURN_EXTRACT_VAR(m_decoder->m_severity); case TYPE_SEVERITY_STR: str = m_decoder->get_severity_str(); RETURN_EXTRACT_CSTR(str); case TYPE_MESSAGE: RETURN_EXTRACT_STRING(m_decoder->m_msg); default: ASSERT(false); return NULL; } } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_container implementation /////////////////////////////////////////////////////////////////////////////// const filtercheck_field_info sinsp_filter_check_container_fields[] = { {PT_CHARBUF, EPF_NONE, PF_NA, "container.id", "the container id."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.name", "the container name."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.image", "the container image name (e.g. sysdig/sysdig:latest for docker, )."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.image.id", "the container image id (e.g. 6f7e2741b66b)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.type", "the container type, eg: docker or rkt"}, {PT_BOOL, EPF_NONE, PF_NA, "container.privileged", "true for containers running as privileged, false otherwise"}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.mounts", "A space-separated list of mount information. Each item in the list has the format ::::"}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "container.mount", "Information about a single mount, specified by number (e.g. container.mount[0]) or mount source (container.mount[/usr/local]). The pathname can be a glob (container.mount[/usr/local/*]), in which case the first matching mount will be returned. The information has the format ::::. If there is no mount with the specified index or matching the provided source, returns the string \"none\" instead of a NULL value."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "container.mount.source", "the mount source, specified by number (e.g. container.mount.source[0]) or mount destination (container.mount.source[/host/lib/modules]). The pathname can be a glob."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "container.mount.dest", "the mount destination, specified by number (e.g. container.mount.dest[0]) or mount source (container.mount.dest[/lib/modules]). The pathname can be a glob."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "container.mount.mode", "the mount mode, specified by number (e.g. container.mount.mode[0]) or mount source (container.mount.mode[/usr/local]). The pathname can be a glob."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "container.mount.rdwr", "the mount rdwr value, specified by number (e.g. container.mount.rdwr[0]) or mount source (container.mount.rdwr[/usr/local]). The pathname can be a glob."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "container.mount.propagation", "the mount propagation value, specified by number (e.g. container.mount.propagation[0]) or mount source (container.mount.propagation[/usr/local]). The pathname can be a glob."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.image.repository", "the container image repository (e.g. sysdig/sysdig)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.image.tag", "the container image tag (e.g. stable, latest)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.image.digest", "the container image registry digest (e.g. sha256:d977378f890d445c15e51795296e4e5062f109ce6da83e0a355fc4ad8699d27)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.healthcheck", "The container's health check. Will be the null value (\"N/A\") if no healthcheck configured, \"NONE\" if configured but explicitly not created, and the healthcheck command line otherwise"}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.liveness_probe", "The container's liveness probe. Will be the null value (\"N/A\") if no liveness probe configured, the liveness probe command line otherwise"}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.readiness_probe", "The container's readiness probe. Will be the null value (\"N/A\") if no readiness probe configured, the readiness probe command line otherwise"} }; sinsp_filter_check_container::sinsp_filter_check_container() { m_info.m_name = "container"; m_info.m_fields = sinsp_filter_check_container_fields; m_info.m_nfields = sizeof(sinsp_filter_check_container_fields) / sizeof(sinsp_filter_check_container_fields[0]); m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; } sinsp_filter_check* sinsp_filter_check_container::allocate_new() { return (sinsp_filter_check*) new sinsp_filter_check_container(); } int32_t sinsp_filter_check_container::extract_arg(const string &val, size_t basepos) { size_t start = val.find_first_of('[', basepos); if(start == string::npos) { throw sinsp_exception("filter syntax error: " + val); } size_t end = val.find_first_of(']', start); if(end == string::npos) { throw sinsp_exception("filter syntax error: " + val); } string numstr = val.substr(start + 1, end-start-1); try { m_argid = sinsp_numparser::parsed32(numstr); } catch (sinsp_exception &e) { if(strstr(e.what(), "is not a valid number") == NULL) { throw; } m_argid = -1; m_argstr = numstr; } return end+1; } int32_t sinsp_filter_check_container::parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) { string val(str); int32_t res = 0; size_t basepos = sizeof("container.mount"); // container.mount. fields allow for indexing by number or source/dest mount path. if(val.find("container.mount.") == 0) { // Note--basepos includes the trailing null, which is // equivalent to the trailing '.' here. if(val.find("source", basepos) == basepos) { m_field_id = TYPE_CONTAINER_MOUNT_SOURCE; } else if(val.find("dest", basepos) == basepos) { m_field_id = TYPE_CONTAINER_MOUNT_DEST; } else if(val.find("mode", basepos) == basepos) { m_field_id = TYPE_CONTAINER_MOUNT_MODE; } else if(val.find("rdwr", basepos) == basepos) { m_field_id = TYPE_CONTAINER_MOUNT_RDWR; } else if(val.find("propagation", basepos) == basepos) { m_field_id = TYPE_CONTAINER_MOUNT_PROPAGATION; } else { throw sinsp_exception("filter syntax error: " + val); } m_field = &m_info.m_fields[m_field_id]; res = extract_arg(val, basepos); } else if (val.find("container.mount") == 0 && val[basepos-1] != 's') { m_field_id = TYPE_CONTAINER_MOUNT; m_field = &m_info.m_fields[m_field_id]; res = extract_arg(val, basepos-1); } else { res = sinsp_filter_check::parse_field_name(str, alloc_state, needed_for_filtering); } return res; } uint8_t* sinsp_filter_check_container::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = 0; sinsp_threadinfo* tinfo = evt->get_thread_info(); if(tinfo == NULL) { return NULL; } switch(m_field_id) { case TYPE_CONTAINER_ID: if(tinfo->m_container_id.empty()) { m_tstr = "host"; } else { m_tstr = tinfo->m_container_id; } RETURN_EXTRACT_STRING(m_tstr); case TYPE_CONTAINER_NAME: if(tinfo->m_container_id.empty()) { m_tstr = "host"; } else { const sinsp_container_info *container_info = m_inspector->m_container_manager.get_container(tinfo->m_container_id); if(!container_info) { return NULL; } if(container_info->m_name.empty()) { return NULL; } m_tstr = container_info->m_name; } RETURN_EXTRACT_STRING(m_tstr); case TYPE_CONTAINER_IMAGE: if(tinfo->m_container_id.empty()) { return NULL; } else { const sinsp_container_info *container_info = m_inspector->m_container_manager.get_container(tinfo->m_container_id); if(!container_info) { return NULL; } if(container_info->m_image.empty()) { return NULL; } m_tstr = container_info->m_image; } RETURN_EXTRACT_STRING(m_tstr); case TYPE_CONTAINER_IMAGE_ID: case TYPE_CONTAINER_IMAGE_REPOSITORY: case TYPE_CONTAINER_IMAGE_TAG: case TYPE_CONTAINER_IMAGE_DIGEST: if(tinfo->m_container_id.empty()) { return NULL; } else { const sinsp_container_info *container_info = m_inspector->m_container_manager.get_container(tinfo->m_container_id); if(!container_info) { return NULL; } const string *field; switch(m_field_id) { case TYPE_CONTAINER_IMAGE_ID: field = &container_info->m_imageid; break; case TYPE_CONTAINER_IMAGE_REPOSITORY: field = &container_info->m_imagerepo; break; case TYPE_CONTAINER_IMAGE_TAG: field = &container_info->m_imagetag; break; case TYPE_CONTAINER_IMAGE_DIGEST: field = &container_info->m_imagedigest; break; default: break; } if(field->empty()) { return NULL; } m_tstr = *field; } RETURN_EXTRACT_STRING(m_tstr); case TYPE_CONTAINER_TYPE: if(tinfo->m_container_id.empty()) { m_tstr = "host"; } else { const sinsp_container_info *container_info = m_inspector->m_container_manager.get_container(tinfo->m_container_id); if(!container_info) { return NULL; } switch(container_info->m_type) { case sinsp_container_type::CT_DOCKER: m_tstr = "docker"; break; case sinsp_container_type::CT_LXC: m_tstr = "lxc"; break; case sinsp_container_type::CT_LIBVIRT_LXC: m_tstr = "libvirt-lxc"; break; case sinsp_container_type::CT_MESOS: m_tstr = "mesos"; break; case sinsp_container_type::CT_CRI: m_tstr = "cri"; break; case sinsp_container_type::CT_CONTAINERD: m_tstr = "containerd"; break; case sinsp_container_type::CT_CRIO: m_tstr = "cri-o"; break; case sinsp_container_type::CT_RKT: m_tstr = "rkt"; break; case sinsp_container_type::CT_BPM: m_tstr = "bpm"; break; default: ASSERT(false); break; } } RETURN_EXTRACT_STRING(m_tstr); case TYPE_CONTAINER_PRIVILEGED: if(tinfo->m_container_id.empty()) { return NULL; } else { const sinsp_container_info *container_info = m_inspector->m_container_manager.get_container(tinfo->m_container_id); if(!container_info) { return NULL; } // Only return a true/false value for // container types where we really know the // privileged status. if (!is_docker_compatible(container_info->m_type)) { return NULL; } m_u32val = (container_info->m_privileged ? 1 : 0); } RETURN_EXTRACT_VAR(m_u32val); break; case TYPE_CONTAINER_MOUNTS: if(tinfo->m_container_id.empty()) { return NULL; } else { const sinsp_container_info *container_info = m_inspector->m_container_manager.get_container(tinfo->m_container_id); if(!container_info) { return NULL; } m_tstr = ""; bool first = true; for(auto &mntinfo : container_info->m_mounts) { if(first) { first = false; } else { m_tstr += ","; } m_tstr += mntinfo.to_string(); } RETURN_EXTRACT_STRING(m_tstr); } break; case TYPE_CONTAINER_MOUNT: if(tinfo->m_container_id.empty()) { return NULL; } else { const sinsp_container_info *container_info = m_inspector->m_container_manager.get_container(tinfo->m_container_id); if(!container_info) { return NULL; } const sinsp_container_info::container_mount_info *mntinfo; if(m_argid != -1) { mntinfo = container_info->mount_by_idx(m_argid); } else { mntinfo = container_info->mount_by_source(m_argstr); } if(!mntinfo) { return NULL; } else { m_tstr = mntinfo->to_string(); } RETURN_EXTRACT_STRING(m_tstr); } break; case TYPE_CONTAINER_MOUNT_SOURCE: case TYPE_CONTAINER_MOUNT_DEST: case TYPE_CONTAINER_MOUNT_MODE: case TYPE_CONTAINER_MOUNT_RDWR: case TYPE_CONTAINER_MOUNT_PROPAGATION: if(tinfo->m_container_id.empty()) { return NULL; } else { const sinsp_container_info *container_info = m_inspector->m_container_manager.get_container(tinfo->m_container_id); if(!container_info) { return NULL; } const sinsp_container_info::container_mount_info *mntinfo; if(m_argid != -1) { mntinfo = container_info->mount_by_idx(m_argid); } else { if (m_field_id == TYPE_CONTAINER_MOUNT_SOURCE) { mntinfo = container_info->mount_by_dest(m_argstr); } else { mntinfo = container_info->mount_by_source(m_argstr); } } if(!mntinfo) { return NULL; } switch (m_field_id) { case TYPE_CONTAINER_MOUNT_SOURCE: m_tstr = mntinfo->m_source; break; case TYPE_CONTAINER_MOUNT_DEST: m_tstr = mntinfo->m_dest; break; case TYPE_CONTAINER_MOUNT_MODE: m_tstr = mntinfo->m_mode; break; case TYPE_CONTAINER_MOUNT_RDWR: m_tstr = (mntinfo->m_rdwr ? "true" : "false"); break; case TYPE_CONTAINER_MOUNT_PROPAGATION: m_tstr = mntinfo->m_propagation; break; } RETURN_EXTRACT_STRING(m_tstr); } break; case TYPE_CONTAINER_HEALTHCHECK: case TYPE_CONTAINER_LIVENESS_PROBE: case TYPE_CONTAINER_READINESS_PROBE: if(tinfo->m_container_id.empty()) { return NULL; } else { const sinsp_container_info *container_info = m_inspector->m_container_manager.get_container(tinfo->m_container_id); if(!container_info) { return NULL; } for(auto &probe : container_info->m_health_probes) { if((m_field_id == TYPE_CONTAINER_HEALTHCHECK && probe.m_probe_type == sinsp_container_info::container_health_probe::PT_HEALTHCHECK) || (m_field_id == TYPE_CONTAINER_LIVENESS_PROBE && probe.m_probe_type == sinsp_container_info::container_health_probe::PT_LIVENESS_PROBE) || (m_field_id == TYPE_CONTAINER_READINESS_PROBE && probe.m_probe_type == sinsp_container_info::container_health_probe::PT_READINESS_PROBE)) { m_tstr = probe.m_health_probe_exe; for(auto &arg : probe.m_health_probe_args) { m_tstr += " "; m_tstr += arg; } RETURN_EXTRACT_STRING(m_tstr); } } // If here, then the container didn't have any // health probe matching the filtercheck // field. m_tstr = "NONE"; RETURN_EXTRACT_STRING(m_tstr); } default: ASSERT(false); break; } return NULL; } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_reference implementation /////////////////////////////////////////////////////////////////////////////// sinsp_filter_check_reference::sinsp_filter_check_reference() { m_info.m_name = ""; m_info.m_fields = &m_finfo; m_info.m_nfields = 1; m_info.m_flags = 0; m_finfo.m_print_format = PF_DEC; m_field = &m_finfo; } sinsp_filter_check* sinsp_filter_check_reference::allocate_new() { ASSERT(false); return NULL; } int32_t sinsp_filter_check_reference::parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) { ASSERT(false); return -1; } uint8_t* sinsp_filter_check_reference::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = m_len; return m_val; } // // convert a number into a byte representation. // E.g. 1230 becomes 1.23K // char* sinsp_filter_check_reference::format_bytes(double val, uint32_t str_len, bool is_int) { char* pr_fmt; if(is_int) { pr_fmt = (char*)"%*.0lf%c"; } else { pr_fmt = (char*)"%*.2lf%c"; } if(val > (1024LL * 1024 * 1024 * 1024 * 1024)) { snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), pr_fmt, str_len - 1, (val) / (1024LL * 1024 * 1024 * 1024 * 1024), 'P'); } else if(val > (1024LL * 1024 * 1024 * 1024)) { snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), pr_fmt, str_len - 1, (val) / (1024LL * 1024 * 1024 * 1024), 'T'); } else if(val > (1024LL * 1024 * 1024)) { snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), pr_fmt, str_len - 1, (val) / (1024LL * 1024 * 1024), 'G'); } else if(val > (1024 * 1024)) { snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), pr_fmt, str_len - 1, (val) / (1024 * 1024), 'M'); } else if(val > 1024) { snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), pr_fmt, str_len - 1, (val) / (1024), 'K'); } else { snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), pr_fmt, str_len, val, 0); } uint32_t len = (uint32_t)strlen(m_getpropertystr_storage); if(len > str_len) { memmove(m_getpropertystr_storage, m_getpropertystr_storage + len - str_len, str_len + 1); // include trailing \0 } return m_getpropertystr_storage; } // // convert a nanosecond time interval into a s.ns representation. // E.g. 1100000000 becomes 1.1s // #define ONE_MILLISECOND_IN_NS 1000000 #define ONE_MICROSECOND_IN_NS 1000 char* sinsp_filter_check_reference::format_time(uint64_t val, uint32_t str_len) { if(val >= ONE_SECOND_IN_NS) { snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), "%u.%02us", (unsigned int)(val / ONE_SECOND_IN_NS), (unsigned int)((val % ONE_SECOND_IN_NS) / 10000000)); } else if(val >= ONE_SECOND_IN_NS / 100) { snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), "%ums", (unsigned int)(val / (ONE_SECOND_IN_NS / 1000))); } else if(val >= ONE_SECOND_IN_NS / 1000) { snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), "%u.%02ums", (unsigned int)(val / (ONE_SECOND_IN_NS / 1000)), (unsigned int)((val % ONE_MILLISECOND_IN_NS) / 10000)); } else if(val >= ONE_SECOND_IN_NS / 100000) { snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), "%uus", (unsigned int)(val / (ONE_SECOND_IN_NS / 1000000))); } else if(val >= ONE_SECOND_IN_NS / 1000000) { snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), "%u.%02uus", (unsigned int)(val / (ONE_SECOND_IN_NS / 1000000)), (unsigned int)((val % ONE_MICROSECOND_IN_NS) / 10)); } else { snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), "%uns", (unsigned int)val); } uint32_t reslen = (uint32_t)strlen(m_getpropertystr_storage); if(reslen < str_len) { uint32_t padding_size = str_len - reslen; memmove(m_getpropertystr_storage + padding_size, m_getpropertystr_storage, str_len + 1); for(uint32_t j = 0; j < padding_size; j++) { m_getpropertystr_storage[j] = ' '; } } return m_getpropertystr_storage; } char* sinsp_filter_check_reference::print_double(uint8_t* rawval, uint32_t str_len) { double val; switch(m_field->m_type) { case PT_INT8: val = (double)*(int8_t*)rawval; break; case PT_INT16: val = (double)*(int16_t*)rawval; break; case PT_INT32: val = (double)*(int32_t*)rawval; break; case PT_INT64: val = (double)*(int64_t*)rawval; break; case PT_UINT8: val = (double)*(uint8_t*)rawval; break; case PT_UINT16: val = (double)*(uint16_t*)rawval; break; case PT_UINT32: val = (double)*(uint32_t*)rawval; break; case PT_UINT64: val = (double)*(uint64_t*)rawval; break; default: ASSERT(false); val = 0; break; } if(m_cnt > 1) { val /= m_cnt; } if(m_print_format == PF_ID) { snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), "%*lf", str_len, val); return m_getpropertystr_storage; } else { return format_bytes(val, str_len, false); } } char* sinsp_filter_check_reference::print_int(uint8_t* rawval, uint32_t str_len) { int64_t val; switch(m_field->m_type) { case PT_INT8: val = (int64_t)*(int8_t*)rawval; break; case PT_INT16: val = (int64_t)*(int16_t*)rawval; break; case PT_INT32: val = (int64_t)*(int32_t*)rawval; break; case PT_INT64: val = (int64_t)*(int64_t*)rawval; break; case PT_UINT8: val = (int64_t)*(uint8_t*)rawval; break; case PT_UINT16: val = (int64_t)*(uint16_t*)rawval; break; case PT_UINT32: val = (int64_t)*(uint32_t*)rawval; break; case PT_UINT64: val = (int64_t)*(uint64_t*)rawval; break; default: ASSERT(false); val = 0; break; } if(m_cnt > 1) { val /= (int64_t)m_cnt; } if(m_print_format == PF_ID) { snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), "%*" PRId64, str_len, val); return m_getpropertystr_storage; } else { return format_bytes((double)val, str_len, true); } } char* sinsp_filter_check_reference::tostring_nice(sinsp_evt* evt, uint32_t str_len, uint64_t time_delta) { uint32_t len; uint8_t* rawval = extract(evt, &len); if(rawval == NULL) { return NULL; } if(time_delta != 0) { m_cnt = (double)time_delta / ONE_SECOND_IN_NS; } if(m_field->m_type >= PT_INT8 && m_field->m_type <= PT_UINT64) { if(m_print_format == PF_ID || m_cnt == 1 || m_cnt == 0) { return print_int(rawval, str_len); } else { return print_double(rawval, str_len); } } else if(m_field->m_type == PT_RELTIME) { double val = (double)*(uint64_t*)rawval; if(m_cnt > 1) { val /= m_cnt; } return format_time((int64_t)val, str_len); } else if(m_field->m_type == PT_DOUBLE) { double dval = (double)*(double*)rawval; if(m_cnt > 1) { dval /= m_cnt; } snprintf(m_getpropertystr_storage, sizeof(m_getpropertystr_storage), "%*.2lf", str_len, dval); return m_getpropertystr_storage; } else { return rawval_to_string(rawval, m_field->m_type, m_field->m_print_format, len); } } Json::Value sinsp_filter_check_reference::tojson(sinsp_evt* evt, uint32_t str_len, uint64_t time_delta) { uint32_t len; uint8_t* rawval = extract(evt, &len); if(rawval == NULL) { return ""; } if(time_delta != 0) { m_cnt = (double)time_delta / ONE_SECOND_IN_NS; } if(m_field->m_type == PT_RELTIME) { double val = (double)*(uint64_t*)rawval; if(m_cnt > 1) { val /= m_cnt; } return format_time((int64_t)val, str_len); } else if(m_field->m_type == PT_DOUBLE) { double dval = (double)*(double*)rawval; if(m_cnt > 1) { dval /= m_cnt; } return dval; } else { return rawval_to_json(rawval, m_field->m_type, m_field->m_print_format, len); } } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_utils implementation /////////////////////////////////////////////////////////////////////////////// const filtercheck_field_info sinsp_filter_check_utils_fields[] = { {PT_UINT64, EPF_NONE, PF_ID, "util.cnt", "incremental counter."}, }; sinsp_filter_check_utils::sinsp_filter_check_utils() { m_info.m_name = "util"; m_info.m_fields = sinsp_filter_check_utils_fields; m_info.m_nfields = sizeof(sinsp_filter_check_utils_fields) / sizeof(sinsp_filter_check_utils_fields[0]); m_info.m_flags = filter_check_info::FL_HIDDEN; m_cnt = 0; } sinsp_filter_check* sinsp_filter_check_utils::allocate_new() { return (sinsp_filter_check*) new sinsp_filter_check_utils(); } uint8_t* sinsp_filter_check_utils::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = 0; switch(m_field_id) { case TYPE_CNT: m_cnt++; RETURN_EXTRACT_VAR(m_cnt); default: ASSERT(false); break; } return NULL; } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_fdlist implementation /////////////////////////////////////////////////////////////////////////////// const filtercheck_field_info sinsp_filter_check_fdlist_fields[] = { {PT_CHARBUF, EPF_NONE, PF_ID, "fdlist.nums", "for poll events, this is a comma-separated list of the FD numbers in the 'fds' argument, returned as a string."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fdlist.names", "for poll events, this is a comma-separated list of the FD names in the 'fds' argument, returned as a string."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fdlist.cips", "for poll events, this is a comma-separated list of the client IP addresses in the 'fds' argument, returned as a string."}, {PT_CHARBUF, EPF_NONE, PF_NA, "fdlist.sips", "for poll events, this is a comma-separated list of the server IP addresses in the 'fds' argument, returned as a string."}, {PT_CHARBUF, EPF_NONE, PF_DEC, "fdlist.cports", "for TCP/UDP FDs, for poll events, this is a comma-separated list of the client TCP/UDP ports in the 'fds' argument, returned as a string."}, {PT_CHARBUF, EPF_NONE, PF_DEC, "fdlist.sports", "for poll events, this is a comma-separated list of the server TCP/UDP ports in the 'fds' argument, returned as a string."}, }; sinsp_filter_check_fdlist::sinsp_filter_check_fdlist() { m_info.m_name = "fdlist"; m_info.m_fields = sinsp_filter_check_fdlist_fields; m_info.m_nfields = sizeof(sinsp_filter_check_fdlist_fields) / sizeof(sinsp_filter_check_fdlist_fields[0]); m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; } sinsp_filter_check* sinsp_filter_check_fdlist::allocate_new() { return (sinsp_filter_check*) new sinsp_filter_check_fdlist(); } uint8_t* sinsp_filter_check_fdlist::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = 0; ASSERT(evt); sinsp_evt_param *parinfo; uint16_t etype = evt->get_type(); if(etype == PPME_SYSCALL_POLL_E || etype == PPME_SYSCALL_PPOLL_E) { parinfo = evt->get_param(0); } else if(etype == PPME_SYSCALL_POLL_X || etype == PPME_SYSCALL_PPOLL_X) { parinfo = evt->get_param(1); } else { return NULL; } uint32_t j = 0; char* payload = parinfo->m_val; uint16_t nfds = *(uint16_t *)payload; uint32_t pos = 2; sinsp_threadinfo* tinfo = evt->get_thread_info(); m_strval.clear(); for(j = 0; j < nfds; j++) { bool add_comma = true; int64_t fd = *(int64_t *)(payload + pos); sinsp_fdinfo_t *fdinfo = tinfo->get_fd(fd); switch(m_field_id) { case TYPE_FDNUMS: { m_strval += to_string(fd); } break; case TYPE_FDNAMES: { if(fdinfo != NULL) { if(fdinfo->m_name != "") { m_strval += fdinfo->m_name; } else { m_strval += ""; } } else { m_strval += ""; } } break; case TYPE_CLIENTIPS: { if(fdinfo != NULL) { if(fdinfo->m_type == SCAP_FD_IPV4_SOCK) { inet_ntop(AF_INET, &fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip, m_addrbuff, sizeof(m_addrbuff)); m_strval += m_addrbuff; break; } else if(fdinfo->m_type == SCAP_FD_IPV6_SOCK) { inet_ntop(AF_INET6, fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip.m_b, m_addrbuff, sizeof(m_addrbuff)); m_strval += m_addrbuff; break; } } add_comma = false; } break; case TYPE_SERVERIPS: { if(fdinfo != NULL) { if(fdinfo->m_type == SCAP_FD_IPV4_SOCK) { inet_ntop(AF_INET, &fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip, m_addrbuff, sizeof(m_addrbuff)); m_strval += m_addrbuff; break; } else if(fdinfo->m_type == SCAP_FD_IPV6_SOCK) { inet_ntop(AF_INET6, fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip.m_b, m_addrbuff, sizeof(m_addrbuff)); m_strval += m_addrbuff; break; } else if(fdinfo->m_type == SCAP_FD_IPV4_SERVSOCK) { inet_ntop(AF_INET, &fdinfo->m_sockinfo.m_ipv4serverinfo.m_ip, m_addrbuff, sizeof(m_addrbuff)); m_strval += m_addrbuff; break; } else if(fdinfo->m_type == SCAP_FD_IPV6_SERVSOCK) { inet_ntop(AF_INET, &fdinfo->m_sockinfo.m_ipv6serverinfo.m_ip.m_b, m_addrbuff, sizeof(m_addrbuff)); m_strval += m_addrbuff; break; } } add_comma = false; } break; case TYPE_CLIENTPORTS: { if(fdinfo != NULL) { if(fdinfo->m_type == SCAP_FD_IPV4_SOCK) { m_strval += to_string(fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport); break; } else if(fdinfo->m_type == SCAP_FD_IPV6_SOCK) { m_strval += to_string(fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sport); break; } } add_comma = false; } case TYPE_SERVERPORTS: { if(fdinfo != NULL) { if(fdinfo->m_type == SCAP_FD_IPV4_SOCK) { m_strval += to_string(fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport); break; } else if(fdinfo->m_type == SCAP_FD_IPV6_SOCK) { m_strval += to_string(fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport); break; } } add_comma = false; } break; default: ASSERT(false); } if(j < nfds && add_comma) { m_strval += ","; } pos += 10; } if(m_strval.size() != 0) { if(m_strval.back() == ',') { m_strval = m_strval.substr(0, m_strval.size() - 1); } RETURN_EXTRACT_STRING(m_strval); } else { return NULL; } } #ifndef CYGWING_AGENT /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_k8s implementation /////////////////////////////////////////////////////////////////////////////// const filtercheck_field_info sinsp_filter_check_k8s_fields[] = { {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.pod.name", "Kubernetes pod name."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.pod.id", "Kubernetes pod id."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "k8s.pod.label", "Kubernetes pod label. E.g. 'k8s.pod.label.foo'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.pod.labels", "Kubernetes pod comma-separated key/value labels. E.g. 'foo1:bar1,foo2:bar2'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.rc.name", "Kubernetes replication controller name."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.rc.id", "Kubernetes replication controller id."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "k8s.rc.label", "Kubernetes replication controller label. E.g. 'k8s.rc.label.foo'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.rc.labels", "Kubernetes replication controller comma-separated key/value labels. E.g. 'foo1:bar1,foo2:bar2'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.svc.name", "Kubernetes service name (can return more than one value, concatenated)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.svc.id", "Kubernetes service id (can return more than one value, concatenated)."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "k8s.svc.label", "Kubernetes service label. E.g. 'k8s.svc.label.foo' (can return more than one value, concatenated)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.svc.labels", "Kubernetes service comma-separated key/value labels. E.g. 'foo1:bar1,foo2:bar2'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.ns.name", "Kubernetes namespace name."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.ns.id", "Kubernetes namespace id."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "k8s.ns.label", "Kubernetes namespace label. E.g. 'k8s.ns.label.foo'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.ns.labels", "Kubernetes namespace comma-separated key/value labels. E.g. 'foo1:bar1,foo2:bar2'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.rs.name", "Kubernetes replica set name."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.rs.id", "Kubernetes replica set id."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "k8s.rs.label", "Kubernetes replica set label. E.g. 'k8s.rs.label.foo'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.rs.labels", "Kubernetes replica set comma-separated key/value labels. E.g. 'foo1:bar1,foo2:bar2'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.deployment.name", "Kubernetes deployment name."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.deployment.id", "Kubernetes deployment id."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "k8s.deployment.label", "Kubernetes deployment label. E.g. 'k8s.rs.label.foo'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "k8s.deployment.labels", "Kubernetes deployment comma-separated key/value labels. E.g. 'foo1:bar1,foo2:bar2'."}, }; sinsp_filter_check_k8s::sinsp_filter_check_k8s() { m_info.m_name = "k8s"; m_info.m_fields = sinsp_filter_check_k8s_fields; m_info.m_nfields = sizeof(sinsp_filter_check_k8s_fields) / sizeof(sinsp_filter_check_k8s_fields[0]); m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; } sinsp_filter_check* sinsp_filter_check_k8s::allocate_new() { return (sinsp_filter_check*) new sinsp_filter_check_k8s(); } int32_t sinsp_filter_check_k8s::parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) { string val(str); if(string(val, 0, sizeof("k8s.pod.label") - 1) == "k8s.pod.label" && string(val, 0, sizeof("k8s.pod.labels") - 1) != "k8s.pod.labels") { m_field_id = TYPE_K8S_POD_LABEL; m_field = &m_info.m_fields[m_field_id]; return extract_arg("k8s.pod.label", val); } else if(string(val, 0, sizeof("k8s.rc.label") - 1) == "k8s.rc.label" && string(val, 0, sizeof("k8s.rc.labels") - 1) != "k8s.rc.labels") { m_field_id = TYPE_K8S_RC_LABEL; m_field = &m_info.m_fields[m_field_id]; return extract_arg("k8s.rc.label", val); } else if(string(val, 0, sizeof("k8s.rs.label") - 1) == "k8s.rs.label" && string(val, 0, sizeof("k8s.rs.labels") - 1) != "k8s.rs.labels") { m_field_id = TYPE_K8S_RS_LABEL; m_field = &m_info.m_fields[m_field_id]; return extract_arg("k8s.rs.label", val); } else if(string(val, 0, sizeof("k8s.svc.label") - 1) == "k8s.svc.label" && string(val, 0, sizeof("k8s.svc.labels") - 1) != "k8s.svc.labels") { m_field_id = TYPE_K8S_SVC_LABEL; m_field = &m_info.m_fields[m_field_id]; return extract_arg("k8s.svc.label", val); } else if(string(val, 0, sizeof("k8s.ns.label") - 1) == "k8s.ns.label" && string(val, 0, sizeof("k8s.ns.labels") - 1) != "k8s.ns.labels") { m_field_id = TYPE_K8S_NS_LABEL; m_field = &m_info.m_fields[m_field_id]; return extract_arg("k8s.ns.label", val); } else if(string(val, 0, sizeof("k8s.deployment.label") - 1) == "k8s.deployment.label" && string(val, 0, sizeof("k8s.deployment.labels") - 1) != "k8s.deployment.labels") { m_field_id = TYPE_K8S_DEPLOYMENT_LABEL; m_field = &m_info.m_fields[m_field_id]; return extract_arg("k8s.deployment.label", val); } else { return sinsp_filter_check::parse_field_name(str, alloc_state, needed_for_filtering); } } int32_t sinsp_filter_check_k8s::extract_arg(const string& fldname, const string& val) { int32_t parsed_len = 0; if(val[fldname.size()] == '.') { size_t endpos; for(endpos = fldname.size() + 1; endpos < val.length(); ++endpos) { if(!isalnum(val[endpos]) && val[endpos] != '/' && val[endpos] != '_' && val[endpos] != '-' && val[endpos] != '.') { break; } } parsed_len = (uint32_t)endpos; m_argname = val.substr(fldname.size() + 1, endpos - fldname.size() - 1); } else { throw sinsp_exception("filter syntax error: " + val); } return parsed_len; } #ifdef HAS_ANALYZER // When using the analyzer, the necessary state is not collected, so // these methods all return no info. const k8s_pod_t* sinsp_filter_check_k8s::find_pod_for_thread(const sinsp_threadinfo* tinfo) { return NULL; } const k8s_ns_t* sinsp_filter_check_k8s::find_ns_by_name(const string& ns_name) { return NULL; } const k8s_rc_t* sinsp_filter_check_k8s::find_rc_by_pod(const k8s_pod_t* pod) { return NULL; } const k8s_rs_t* sinsp_filter_check_k8s::find_rs_by_pod(const k8s_pod_t* pod) { return NULL; } vector sinsp_filter_check_k8s::find_svc_by_pod(const k8s_pod_t* pod) { vector empty; return empty; } const k8s_deployment_t* sinsp_filter_check_k8s::find_deployment_by_pod(const k8s_pod_t* pod) { return NULL; } #else const k8s_pod_t* sinsp_filter_check_k8s::find_pod_for_thread(const sinsp_threadinfo* tinfo) { if(tinfo->m_container_id.empty()) { return NULL; } const k8s_state_t& k8s_state = m_inspector->m_k8s_client->get_state(); return k8s_state.get_pod(tinfo->m_container_id); } const k8s_ns_t* sinsp_filter_check_k8s::find_ns_by_name(const string& ns_name) { const k8s_state_t& k8s_state = m_inspector->m_k8s_client->get_state(); const k8s_state_t::namespace_map& ns_map = k8s_state.get_namespace_map(); k8s_state_t::namespace_map::const_iterator it = ns_map.find(ns_name); if(it != ns_map.end()) { return it->second; } return NULL; } const k8s_rc_t* sinsp_filter_check_k8s::find_rc_by_pod(const k8s_pod_t* pod) { const k8s_state_t& k8s_state = m_inspector->m_k8s_client->get_state(); const k8s_state_t::pod_rc_map& pod_rcs = k8s_state.get_pod_rc_map(); k8s_state_t::pod_rc_map::const_iterator it = pod_rcs.find(pod->get_uid()); if(it != pod_rcs.end()) { return it->second; } return NULL; } const k8s_rs_t* sinsp_filter_check_k8s::find_rs_by_pod(const k8s_pod_t* pod) { const k8s_state_t& k8s_state = m_inspector->m_k8s_client->get_state(); const k8s_state_t::pod_rs_map& pod_rss = k8s_state.get_pod_rs_map(); k8s_state_t::pod_rs_map::const_iterator it = pod_rss.find(pod->get_uid()); if(it != pod_rss.end()) { return it->second; } return NULL; } vector sinsp_filter_check_k8s::find_svc_by_pod(const k8s_pod_t* pod) { const k8s_state_t& k8s_state = m_inspector->m_k8s_client->get_state(); vector services; const k8s_state_t::pod_service_map& pod_services = k8s_state.get_pod_service_map(); auto range = pod_services.equal_range(pod->get_uid()); for(auto it = range.first; it != range.second; ++it) { services.push_back(it->second); } return services; } const k8s_deployment_t* sinsp_filter_check_k8s::find_deployment_by_pod(const k8s_pod_t* pod) { const k8s_state_t& k8s_state = m_inspector->m_k8s_client->get_state(); const k8s_state_t::pod_deployment_map& pod_deployments = k8s_state.get_pod_deployment_map(); k8s_state_t::pod_deployment_map::const_iterator it = pod_deployments.find(pod->get_uid()); if(it != pod_deployments.end()) { return it->second; } return NULL; } #endif void sinsp_filter_check_k8s::concatenate_labels(const k8s_pair_list& labels, string* s) { for(const k8s_pair_t& label_pair : labels) { if(!s->empty()) { s->append(", "); } s->append(label_pair.first); if(!label_pair.second.empty()) { s->append(":" + label_pair.second); } } } bool sinsp_filter_check_k8s::find_label(const k8s_pair_list& labels, const string& key, string* value) { for(const k8s_pair_t& label_pair : labels) { if(label_pair.first == key) { *value = label_pair.second; return true; } } return false; } uint8_t* sinsp_filter_check_k8s::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = 0; if(m_inspector->m_k8s_client == NULL) { return NULL; } ASSERT(evt); if(evt == NULL) { ASSERT(false); return NULL; } sinsp_threadinfo* tinfo = evt->get_thread_info(); if(tinfo == NULL) { return NULL; } const k8s_pod_t* pod = find_pod_for_thread(tinfo); if(pod == NULL) { return NULL; } m_tstr.clear(); switch(m_field_id) { case TYPE_K8S_POD_NAME: m_tstr = pod->get_name(); RETURN_EXTRACT_STRING(m_tstr); case TYPE_K8S_POD_ID: m_tstr = pod->get_uid(); RETURN_EXTRACT_STRING(m_tstr); case TYPE_K8S_POD_LABEL: { if(find_label(pod->get_labels(), m_argname, &m_tstr)) { RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_K8S_POD_LABELS: { concatenate_labels(pod->get_labels(), &m_tstr); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_K8S_RC_NAME: { const k8s_rc_t* rc = find_rc_by_pod(pod); if(rc != NULL) { m_tstr = rc->get_name(); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_K8S_RC_ID: { const k8s_rc_t* rc = find_rc_by_pod(pod); if(rc != NULL) { m_tstr = rc->get_uid(); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_K8S_RC_LABEL: { const k8s_rc_t* rc = find_rc_by_pod(pod); if(rc != NULL) { if(find_label(rc->get_labels(), m_argname, &m_tstr)) { RETURN_EXTRACT_STRING(m_tstr); } } break; } case TYPE_K8S_RC_LABELS: { const k8s_rc_t* rc = find_rc_by_pod(pod); if(rc != NULL) { concatenate_labels(rc->get_labels(), &m_tstr); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_K8S_RS_NAME: { const k8s_rs_t* rs = find_rs_by_pod(pod); if(rs != NULL) { m_tstr = rs->get_name(); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_K8S_RS_ID: { const k8s_rs_t* rs = find_rs_by_pod(pod); if(rs != NULL) { m_tstr = rs->get_uid(); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_K8S_RS_LABEL: { const k8s_rs_t* rs = find_rs_by_pod(pod); if(rs != NULL) { if(find_label(rs->get_labels(), m_argname, &m_tstr)) { RETURN_EXTRACT_STRING(m_tstr); } } break; } case TYPE_K8S_RS_LABELS: { const k8s_rs_t* rs = find_rs_by_pod(pod); if(rs != NULL) { concatenate_labels(rs->get_labels(), &m_tstr); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_K8S_SVC_NAME: { vector services = find_svc_by_pod(pod); if(!services.empty()) { for(const k8s_service_t* service : services) { if(!m_tstr.empty()) { m_tstr.append(", "); } m_tstr.append(service->get_name()); } RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_K8S_SVC_ID: { vector services = find_svc_by_pod(pod); if(!services.empty()) { for(const k8s_service_t* service : services) { if(!m_tstr.empty()) { m_tstr.append(", "); } m_tstr.append(service->get_uid()); } RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_K8S_SVC_LABEL: { vector services = find_svc_by_pod(pod); if(!services.empty()) { for(const k8s_service_t* service : services) { string val; if(find_label(service->get_labels(), m_argname, &val)) { if(!m_tstr.empty()) { m_tstr.append(", "); } m_tstr.append(val); } } if(!m_tstr.empty()) { RETURN_EXTRACT_STRING(m_tstr); } } break; } case TYPE_K8S_SVC_LABELS: { vector services = find_svc_by_pod(pod); if(!services.empty()) { for(const k8s_service_t* service : services) { concatenate_labels(service->get_labels(), &m_tstr); } RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_K8S_NS_NAME: { m_tstr = pod->get_namespace(); RETURN_EXTRACT_STRING(m_tstr); } case TYPE_K8S_NS_ID: { const k8s_ns_t* ns = find_ns_by_name(pod->get_namespace()); if(ns != NULL) { m_tstr = ns->get_uid(); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_K8S_NS_LABEL: { const k8s_ns_t* ns = find_ns_by_name(pod->get_namespace()); if(ns != NULL) { if(find_label(ns->get_labels(), m_argname, &m_tstr)) { RETURN_EXTRACT_STRING(m_tstr); } } break; } case TYPE_K8S_NS_LABELS: { const k8s_ns_t* ns = find_ns_by_name(pod->get_namespace()); if(ns != NULL) { concatenate_labels(ns->get_labels(), &m_tstr); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_K8S_DEPLOYMENT_NAME: { const k8s_deployment_t* deployment = find_deployment_by_pod(pod); if(deployment != NULL) { m_tstr = deployment->get_name(); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_K8S_DEPLOYMENT_ID: { const k8s_deployment_t* deployment = find_deployment_by_pod(pod); if(deployment != NULL) { m_tstr = deployment->get_uid(); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_K8S_DEPLOYMENT_LABEL: { const k8s_deployment_t* deployment = find_deployment_by_pod(pod); if(deployment != NULL) { if(find_label(deployment->get_labels(), m_argname, &m_tstr)) { RETURN_EXTRACT_STRING(m_tstr); } } break; } case TYPE_K8S_DEPLOYMENT_LABELS: { const k8s_deployment_t* deployment = find_deployment_by_pod(pod); if(deployment != NULL) { concatenate_labels(deployment->get_labels(), &m_tstr); RETURN_EXTRACT_STRING(m_tstr); } break; } default: ASSERT(false); return NULL; } return NULL; } #endif // CYGWING_AGENT /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_mesos implementation /////////////////////////////////////////////////////////////////////////////// #ifndef CYGWING_AGENT const filtercheck_field_info sinsp_filter_check_mesos_fields[] = { {PT_CHARBUF, EPF_NONE, PF_NA, "mesos.task.name", "Mesos task name."}, {PT_CHARBUF, EPF_NONE, PF_NA, "mesos.task.id", "Mesos task id."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "mesos.task.label", "Mesos task label. E.g. 'mesos.task.label.foo'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "mesos.task.labels", "Mesos task comma-separated key/value labels. E.g. 'foo1:bar1,foo2:bar2'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "mesos.framework.name", "Mesos framework name."}, {PT_CHARBUF, EPF_NONE, PF_NA, "mesos.framework.id", "Mesos framework id."}, {PT_CHARBUF, EPF_NONE, PF_NA, "marathon.app.name", "Marathon app name."}, {PT_CHARBUF, EPF_NONE, PF_NA, "marathon.app.id", "Marathon app id."}, {PT_CHARBUF, EPF_REQUIRES_ARGUMENT, PF_NA, "marathon.app.label", "Marathon app label. E.g. 'marathon.app.label.foo'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "marathon.app.labels", "Marathon app comma-separated key/value labels. E.g. 'foo1:bar1,foo2:bar2'."}, {PT_CHARBUF, EPF_NONE, PF_NA, "marathon.group.name", "Marathon group name."}, {PT_CHARBUF, EPF_NONE, PF_NA, "marathon.group.id", "Marathon group id."}, }; sinsp_filter_check_mesos::sinsp_filter_check_mesos() { m_info.m_name = "mesos"; m_info.m_fields = sinsp_filter_check_mesos_fields; m_info.m_nfields = sizeof(sinsp_filter_check_mesos_fields) / sizeof(sinsp_filter_check_mesos_fields[0]); m_info.m_flags = filter_check_info::FL_WORKS_ON_THREAD_TABLE; } sinsp_filter_check* sinsp_filter_check_mesos::allocate_new() { return (sinsp_filter_check*) new sinsp_filter_check_mesos(); } int32_t sinsp_filter_check_mesos::parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) { string val(str); if(string(val, 0, sizeof("mesos.task.label") - 1) == "mesos.task.label" && string(val, 0, sizeof("mesos.task.labels") - 1) != "mesos.task.labels") { m_field_id = TYPE_MESOS_TASK_LABEL; m_field = &m_info.m_fields[m_field_id]; return extract_arg("mesos.task.label", val); } else if(string(val, 0, sizeof("marathon.app.label") - 1) == "marathon.app.label" && string(val, 0, sizeof("marathon.app.labels") - 1) != "marathon.app.labels") { m_field_id = TYPE_MARATHON_APP_LABEL; m_field = &m_info.m_fields[m_field_id]; return extract_arg("marathon.app.label", val); } else { return sinsp_filter_check::parse_field_name(str, alloc_state, needed_for_filtering); } } int32_t sinsp_filter_check_mesos::extract_arg(const string& fldname, const string& val) { int32_t parsed_len = 0; if(val[fldname.size()] == '.') { size_t endpos; for(endpos = fldname.size() + 1; endpos < val.length(); ++endpos) { if(!isalnum(val[endpos]) && val[endpos] != '/' && val[endpos] != '_' && val[endpos] != '-' && val[endpos] != '.') { break; } } parsed_len = (uint32_t)endpos; m_argname = val.substr(fldname.size() + 1, endpos - fldname.size() - 1); } else { throw sinsp_exception("filter syntax error: " + val); } return parsed_len; } mesos_task::ptr_t sinsp_filter_check_mesos::find_task_for_thread(const sinsp_threadinfo* tinfo) { ASSERT(m_inspector && tinfo); if(tinfo) { if(tinfo->m_container_id.empty()) { return NULL; } if(m_inspector && m_inspector->m_mesos_client) { const sinsp_container_info *container_info = m_inspector->m_container_manager.get_container(tinfo->m_container_id); if(!container_info || container_info->m_mesos_task_id.empty()) { return NULL; } const mesos_state_t& mesos_state = m_inspector->m_mesos_client->get_state(); return mesos_state.get_task(container_info->m_mesos_task_id); } } return NULL; } const mesos_framework* sinsp_filter_check_mesos::find_framework_by_task(mesos_task::ptr_t task) { if(task && m_inspector && m_inspector->m_mesos_client) { const mesos_state_t& mesos_state = m_inspector->m_mesos_client->get_state(); return mesos_state.get_framework_for_task(task->get_uid()); } return NULL; } marathon_app::ptr_t sinsp_filter_check_mesos::find_app_by_task(mesos_task::ptr_t task) { if(m_inspector && m_inspector->m_mesos_client) { return m_inspector->m_mesos_client->get_state().get_app(task); } return NULL; } marathon_group::ptr_t sinsp_filter_check_mesos::find_group_by_task(mesos_task::ptr_t task) { if(m_inspector && m_inspector->m_mesos_client) { return m_inspector->m_mesos_client->get_state().get_group(task); } return NULL; } void sinsp_filter_check_mesos::concatenate_labels(const mesos_pair_list& labels, string* s) { for(const mesos_pair_t& label_pair : labels) { if(!s->empty()) { s->append(", "); } s->append(label_pair.first); if(!label_pair.second.empty()) { s->append(":" + label_pair.second); } } } bool sinsp_filter_check_mesos::find_label(const mesos_pair_list& labels, const string& key, string* value) { for(const mesos_pair_t& label_pair : labels) { if(label_pair.first == key) { *value = label_pair.second; return true; } } return false; } uint8_t* sinsp_filter_check_mesos::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { *len = 0; if(!m_inspector || !m_inspector->m_mesos_client) { return NULL; } if(!evt) { ASSERT(false); return NULL; } sinsp_threadinfo* tinfo = evt->get_thread_info(); if(!tinfo) { return NULL; } mesos_task::ptr_t task = find_task_for_thread(tinfo); if(!task) { return NULL; } m_tstr.clear(); switch(m_field_id) { case TYPE_MESOS_TASK_NAME: m_tstr = task->get_name(); RETURN_EXTRACT_STRING(m_tstr); case TYPE_MESOS_TASK_ID: m_tstr = task->get_uid(); RETURN_EXTRACT_STRING(m_tstr); case TYPE_MESOS_TASK_LABEL: if(find_label(task->get_labels(), m_argname, &m_tstr)) { RETURN_EXTRACT_STRING(m_tstr); } break; case TYPE_MESOS_TASK_LABELS: concatenate_labels(task->get_labels(), &m_tstr); RETURN_EXTRACT_STRING(m_tstr); case TYPE_MESOS_FRAMEWORK_NAME: { const mesos_framework* fw = find_framework_by_task(task); if(fw) { m_tstr = fw->get_name(); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_MESOS_FRAMEWORK_ID: { const mesos_framework* fw = find_framework_by_task(task); if(fw) { m_tstr = fw->get_uid(); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_MARATHON_APP_NAME: { marathon_app::ptr_t app = find_app_by_task(task); if(app != NULL) { m_tstr = app->get_name(); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_MARATHON_APP_ID: { marathon_app::ptr_t app = find_app_by_task(task); if(app != NULL) { m_tstr = app->get_id(); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_MARATHON_APP_LABEL: { marathon_app::ptr_t app = find_app_by_task(task); if(app && find_label(app->get_labels(), m_argname, &m_tstr)) { RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_MARATHON_APP_LABELS: { marathon_app::ptr_t app = find_app_by_task(task); if(app) { concatenate_labels(app->get_labels(), &m_tstr); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_MARATHON_GROUP_NAME: { marathon_app::ptr_t app = find_app_by_task(task); if(app) { m_tstr = app->get_group_id(); RETURN_EXTRACT_STRING(m_tstr); } break; } case TYPE_MARATHON_GROUP_ID: { marathon_app::ptr_t app = find_app_by_task(task); if(app) { m_tstr = app->get_group_id(); RETURN_EXTRACT_STRING(m_tstr); } break; } default: ASSERT(false); return NULL; } return NULL; } #endif // CYGWING_AGENT #endif // HAS_FILTERING sysdig-0.26.4/userspace/libsinsp/filterchecks.h000066400000000000000000000565241352731327100215610ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #include #include "filter_value.h" #include "prefix_search.h" #ifndef CYGWING_AGENT #include "k8s.h" #include "mesos.h" #endif #ifdef HAS_FILTERING #include "gen_filter.h" class sinsp_filter_check_reference; bool flt_compare(cmpop op, ppm_param_type type, void* operand1, void* operand2, uint32_t op1_len = 0, uint32_t op2_len = 0); bool flt_compare_avg(cmpop op, ppm_param_type type, void* operand1, void* operand2, uint32_t op1_len, uint32_t op2_len, uint32_t cnt1, uint32_t cnt2); bool flt_compare_ipv4net(cmpop op, uint64_t operand1, ipv4net* operand2); bool flt_compare_ipv6net(cmpop op, ipv6addr *operand1, ipv6addr* operand2); char* flt_to_string(uint8_t* rawval, filtercheck_field_info* finfo); int32_t gmt2local(time_t t); class operand_info { public: uint32_t m_id; ppm_param_type m_type; string m_name; string m_description; }; /////////////////////////////////////////////////////////////////////////////// // The filter check interface // NOTE: in order to add a new type of filter check, you need to add a class for // it and then add it to new_filter_check_from_name. /////////////////////////////////////////////////////////////////////////////// class sinsp_filter_check : public gen_event_filter_check { public: sinsp_filter_check(); virtual ~sinsp_filter_check() { } // // Allocate a new check of the same type. // Every filtercheck plugin must implement this. // virtual sinsp_filter_check* allocate_new() = 0; // // Get the list of fields that this check exports // virtual filter_check_info* get_fields() { return &m_info; } // // Parse the name of the field. // Returns the length of the parsed field if successful, an exception in // case of error. // virtual int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering); // // If this check is used by a filter, extract the constant to compare it to // Doesn't return the field length because the filtering engine can calculate it. // void add_filter_value(const char* str, uint32_t len, uint32_t i = 0 ); virtual size_t parse_filter_value(const char* str, uint32_t len, uint8_t *storage, uint32_t storage_len); // // Called after parsing for optional validation of the filter value // void validate_filter_value(const char* str, uint32_t len) {} // // Return the info about the field that this instance contains // virtual const filtercheck_field_info* get_field_info(); // // Extract the field from the event. In sanitize_strings is true, any // string values are sanitized to remove nonprintable characters. // uint8_t* extract(gen_event *evt, OUT uint32_t* len, bool sanitize_strings = true); virtual uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true) = 0; // // Extract the field as json from the event (by default, fall // back to the regular extract functionality) // virtual Json::Value extract_as_js(sinsp_evt *evt, OUT uint32_t* len) { return Json::nullValue; } // // Compare the field with the constant value obtained from parse_filter_value() // bool compare(gen_event *evt); virtual bool compare(sinsp_evt *evt); // // Extract the value from the event and convert it into a string // virtual char* tostring(sinsp_evt* evt); // // Extract the value from the event and convert it into a Json value // or object // virtual Json::Value tojson(sinsp_evt* evt); sinsp* m_inspector; bool m_needs_state_tracking = false; sinsp_field_aggregation m_aggregation; sinsp_field_aggregation m_merge_aggregation; protected: bool flt_compare(cmpop op, ppm_param_type type, void* operand1, uint32_t op1_len = 0, uint32_t op2_len = 0); char* rawval_to_string(uint8_t* rawval, ppm_param_type ptype, ppm_print_format print_format, uint32_t len); Json::Value rawval_to_json(uint8_t* rawval, ppm_param_type ptype, ppm_print_format print_format, uint32_t len); void string_to_rawval(const char* str, uint32_t len, ppm_param_type ptype); char m_getpropertystr_storage[1024]; vector> m_val_storages; inline uint8_t* filter_value_p(uint16_t i = 0) { return &m_val_storages[i][0]; } inline vector filter_value(uint16_t i = 0) { return m_val_storages[i]; } unordered_set m_val_storages_members; path_prefix_search m_val_storages_paths; uint32_t m_val_storages_min_size; uint32_t m_val_storages_max_size; const filtercheck_field_info* m_field; filter_check_info m_info; uint32_t m_field_id; uint32_t m_th_state_id; uint32_t m_val_storage_len; private: void set_inspector(sinsp* inspector); friend class sinsp_filter_check_list; }; // // Global class that stores the list of filtercheck plugins and offers // functions to work with it. // class sinsp_filter_check_list { public: sinsp_filter_check_list(); ~sinsp_filter_check_list(); void add_filter_check(sinsp_filter_check* filter_check); void get_all_fields(vector* list); sinsp_filter_check* new_filter_check_from_another(sinsp_filter_check *chk); sinsp_filter_check* new_filter_check_from_fldname(const string& name, sinsp* inspector, bool do_exact_check); private: vector m_check_list; }; /////////////////////////////////////////////////////////////////////////////// // Filter check classes /////////////////////////////////////////////////////////////////////////////// // // fd checks // class sinsp_filter_check_fd : public sinsp_filter_check { public: enum check_type { TYPE_FDNUM = 0, TYPE_FDTYPE = 1, TYPE_FDTYPECHAR = 2, TYPE_FDNAME = 3, TYPE_DIRECTORY = 4, TYPE_FILENAME = 5, TYPE_IP = 6, TYPE_CLIENTIP = 7, TYPE_SERVERIP = 8, TYPE_LIP = 9, TYPE_RIP = 10, TYPE_PORT = 11, TYPE_CLIENTPORT = 12, TYPE_SERVERPORT = 13, TYPE_LPORT = 14, TYPE_RPORT = 15, TYPE_L4PROTO = 16, TYPE_SOCKFAMILY = 17, TYPE_IS_SERVER = 18, TYPE_UID = 19, TYPE_CONTAINERNAME = 20, TYPE_CONTAINERDIRECTORY = 21, TYPE_PROTO = 22, TYPE_CLIENTPROTO = 23, TYPE_SERVERPROTO = 24, TYPE_LPROTO = 25, TYPE_RPROTO = 26, TYPE_NET = 27, TYPE_CNET = 28, TYPE_SNET = 29, TYPE_LNET = 30, TYPE_RNET = 31, TYPE_IS_CONNECTED = 32, TYPE_NAME_CHANGED = 33, TYPE_CLIENTIP_NAME = 34, TYPE_SERVERIP_NAME = 35, TYPE_LIP_NAME = 36, TYPE_RIP_NAME = 37, TYPE_DEV = 38, TYPE_DEV_MAJOR = 39, TYPE_DEV_MINOR = 40, }; enum fd_type { FDT_NONE, FDT_FILE, FDT_SOCK, FDT_IPV4_SOCK, FDT_IPV6_SOCK, FDT_UNIX_SOCK, FDT_PIPE, FDT_EVENT, FDT_SIGNALFD, FDT_EVENTPOLL, FDT_INOTIFY, FDT_TIMERFD }; sinsp_filter_check_fd(); sinsp_filter_check* allocate_new(); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); bool compare_ip(sinsp_evt *evt); bool compare_net(sinsp_evt *evt); bool compare_port(sinsp_evt *evt); bool compare_domain(sinsp_evt *evt); bool compare(sinsp_evt *evt); sinsp_threadinfo* m_tinfo; sinsp_fdinfo_t* m_fdinfo; fd_type m_fd_type; string m_tstr; uint8_t m_tcstr[2]; uint32_t m_tbool; private: uint8_t* extract_from_null_fd(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings); bool extract_fdname_from_creator(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings); bool extract_fd(sinsp_evt *evt); }; // // thread sinsp_filter_check_thread // class sinsp_filter_check_thread : public sinsp_filter_check { public: enum check_type { TYPE_PID = 0, TYPE_EXE = 1, TYPE_NAME = 2, TYPE_ARGS = 3, TYPE_ENV = 4, TYPE_CMDLINE = 5, TYPE_EXELINE = 6, TYPE_CWD = 7, TYPE_NTHREADS = 8, TYPE_NCHILDS = 9, TYPE_PPID = 10, TYPE_PNAME = 11, TYPE_PCMDLINE = 12, TYPE_APID = 13, TYPE_ANAME = 14, TYPE_LOGINSHELLID = 15, TYPE_DURATION = 16, TYPE_FDOPENCOUNT = 17, TYPE_FDLIMIT = 18, TYPE_FDUSAGE = 19, TYPE_VMSIZE = 20, TYPE_VMRSS = 21, TYPE_VMSWAP = 22, TYPE_PFMAJOR = 23, TYPE_PFMINOR = 24, TYPE_TID = 25, TYPE_ISMAINTHREAD = 26, TYPE_EXECTIME = 27, TYPE_TOTEXECTIME = 28, TYPE_CGROUPS = 29, TYPE_CGROUP = 30, TYPE_VTID = 31, TYPE_VPID = 32, TYPE_THREAD_CPU = 33, TYPE_THREAD_CPU_USER = 34, TYPE_THREAD_CPU_SYSTEM = 35, TYPE_THREAD_VMSIZE = 36, TYPE_THREAD_VMRSS = 37, TYPE_THREAD_VMSIZE_B = 38, TYPE_THREAD_VMRSS_B = 39, TYPE_SID = 40, TYPE_SNAME = 41, TYPE_TTY = 42, TYPE_EXEPATH = 43, TYPE_NAMETID = 44, TYPE_VPGID = 45, TYPE_IS_CONTAINER_HEALTHCHECK = 46, TYPE_IS_CONTAINER_LIVENESS_PROBE = 47, TYPE_IS_CONTAINER_READINESS_PROBE = 48, }; sinsp_filter_check_thread(); sinsp_filter_check* allocate_new(); int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); bool compare(sinsp_evt *evt); private: uint64_t extract_exectime(sinsp_evt *evt); int32_t extract_arg(string fldname, string val, OUT const struct ppm_param_info** parinfo); uint8_t* extract_thread_cpu(sinsp_evt *evt, OUT uint32_t* len, sinsp_threadinfo* tinfo, bool extract_user, bool extract_system); inline bool compare_full_apid(sinsp_evt *evt); bool compare_full_aname(sinsp_evt *evt); int32_t m_argid; string m_argname; uint32_t m_tbool; string m_tstr; uint64_t m_u64val; int64_t m_s64val; double m_dval; vector m_last_proc_switch_times; uint32_t m_th_state_id; uint64_t m_cursec_ts; }; // // event checks // class sinsp_filter_check_event : public sinsp_filter_check { public: enum check_type { TYPE_NUMBER = 0, TYPE_TIME = 1, TYPE_TIME_S = 2, TYPE_TIME_ISO8601 = 3, TYPE_DATETIME = 4, TYPE_RAWTS = 5, TYPE_RAWTS_S = 6, TYPE_RAWTS_NS = 7, TYPE_RELTS = 8, TYPE_RELTS_S = 9, TYPE_RELTS_NS = 10, TYPE_LATENCY = 11, TYPE_LATENCY_S = 12, TYPE_LATENCY_NS = 13, TYPE_LATENCY_QUANTIZED = 14, TYPE_LATENCY_HUMAN = 15, TYPE_DELTA = 16, TYPE_DELTA_S = 17, TYPE_DELTA_NS = 18, TYPE_RUNTIME_TIME_OUTPUT_FORMAT = 19, TYPE_DIR = 20, TYPE_TYPE = 21, TYPE_TYPE_IS = 22, TYPE_SYSCALL_TYPE = 23, TYPE_CATEGORY = 24, TYPE_CPU = 25, TYPE_ARGS = 26, TYPE_ARGSTR = 27, TYPE_ARGRAW = 28, TYPE_INFO = 29, TYPE_BUFFER = 30, TYPE_BUFLEN = 31, TYPE_RESSTR = 32, TYPE_RESRAW = 33, TYPE_FAILED = 34, TYPE_ISIO = 35, TYPE_ISIO_READ = 36, TYPE_ISIO_WRITE = 37, TYPE_IODIR = 38, TYPE_ISWAIT = 39, TYPE_WAIT_LATENCY = 40, TYPE_ISSYSLOG = 41, TYPE_COUNT = 42, TYPE_COUNT_ERROR = 43, TYPE_COUNT_ERROR_FILE = 44, TYPE_COUNT_ERROR_NET = 45, TYPE_COUNT_ERROR_MEMORY = 46, TYPE_COUNT_ERROR_OTHER = 47, TYPE_COUNT_EXIT = 48, TYPE_COUNT_PROCINFO = 49, TYPE_COUNT_THREADINFO = 50, TYPE_AROUND = 51, TYPE_ABSPATH = 52, TYPE_BUFLEN_IN = 53, TYPE_BUFLEN_OUT = 54, TYPE_BUFLEN_FILE = 55, TYPE_BUFLEN_FILE_IN = 56, TYPE_BUFLEN_FILE_OUT = 57, TYPE_BUFLEN_NET = 58, TYPE_BUFLEN_NET_IN = 59, TYPE_BUFLEN_NET_OUT = 60, TYPE_ISOPEN_READ = 61, TYPE_ISOPEN_WRITE = 62, TYPE_INFRA_DOCKER_NAME = 63, TYPE_INFRA_DOCKER_CONTAINER_ID = 64, TYPE_INFRA_DOCKER_CONTAINER_NAME = 65, TYPE_INFRA_DOCKER_CONTAINER_IMAGE = 66, }; sinsp_filter_check_event(); ~sinsp_filter_check_event(); sinsp_filter_check* allocate_new(); int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering); size_t parse_filter_value(const char* str, uint32_t len, uint8_t *storage, uint32_t storage_len); void validate_filter_value(const char* str, uint32_t len); const filtercheck_field_info* get_field_info(); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); Json::Value extract_as_js(sinsp_evt *evt, OUT uint32_t* len); bool compare(sinsp_evt *evt); uint64_t m_u64val; uint64_t m_tsdelta; uint32_t m_u32val; string m_strstorage; string m_argname; int32_t m_argid; uint32_t m_evtid; uint32_t m_evtid1; const ppm_param_info* m_arginfo; // // Note: this copy of the field is used by some fields, like TYPE_ARGS and // TYPE_RESARG, that need to do on the fly type customization // filtercheck_field_info m_customfield; private: int32_t extract_arg(string fldname, string val, OUT const struct ppm_param_info** parinfo); int32_t extract_type(string fldname, string val, OUT const struct ppm_param_info** parinfo); uint8_t* extract_error_count(sinsp_evt *evt, OUT uint32_t* len); uint8_t *extract_abspath(sinsp_evt *evt, OUT uint32_t *len); inline uint8_t* extract_buflen(sinsp_evt *evt, OUT uint32_t* len); bool m_is_compare; char* m_storage; uint32_t m_storage_size; const char* m_cargname; sinsp_filter_check_reference* m_converter; }; // // user checks // class sinsp_filter_check_user : public sinsp_filter_check { public: enum check_type { TYPE_UID = 0, TYPE_NAME = 1, TYPE_HOMEDIR = 2, TYPE_SHELL = 3, TYPE_LOGINUID = 4, TYPE_LOGINNAME = 5, }; sinsp_filter_check_user(); sinsp_filter_check* allocate_new(); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); uint32_t m_uid; string m_strval; }; // // group checks // class sinsp_filter_check_group : public sinsp_filter_check { public: enum check_type { TYPE_GID, TYPE_NAME, }; sinsp_filter_check_group(); sinsp_filter_check* allocate_new(); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); uint32_t m_gid; string m_name; }; // // Tracers // #define TEXT_ARG_ID -1000000 class sinsp_filter_check_tracer : public sinsp_filter_check { public: enum check_type { TYPE_ID = 0, TYPE_TIME, TYPE_NTAGS, TYPE_NARGS, TYPE_TAGS, TYPE_TAG, TYPE_ARGS, TYPE_ARG, TYPE_ENTERARGS, TYPE_ENTERARG, TYPE_DURATION, TYPE_DURATION_QUANTIZED, TYPE_DURATION_HUMAN, TYPE_TAGDURATION, TYPE_COUNT, TYPE_TAGCOUNT, TYPE_TAGCHILDSCOUNT, TYPE_IDTAG, TYPE_RAWTIME, TYPE_RAWPARENTTIME, }; sinsp_filter_check_tracer(); ~sinsp_filter_check_tracer(); sinsp_filter_check* allocate_new(); int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); private: int32_t extract_arg(string fldname, string val, OUT const struct ppm_param_info** parinfo); inline uint8_t* extract_duration(uint16_t etype, sinsp_tracerparser* eparser, OUT uint32_t* len); uint8_t* extract_args(sinsp_partial_tracer* pae, OUT uint32_t *len); uint8_t* extract_arg(sinsp_partial_tracer* pae, OUT uint32_t *len); int32_t m_argid; string m_argname; const char* m_cargname; char* m_storage; uint32_t m_storage_size; int64_t m_s64val; int32_t m_u32val; sinsp_filter_check_reference* m_converter; string m_strstorage; }; // // Events in tracers checks // class sinsp_filter_check_evtin : public sinsp_filter_check { public: enum check_type { TYPE_ID = 0, TYPE_NTAGS, TYPE_NARGS, TYPE_TAGS, TYPE_TAG, TYPE_ARGS, TYPE_ARG, TYPE_P_ID, TYPE_P_NTAGS, TYPE_P_NARGS, TYPE_P_TAGS, TYPE_P_TAG, TYPE_P_ARGS, TYPE_P_ARG, TYPE_S_ID, TYPE_S_NTAGS, TYPE_S_NARGS, TYPE_S_TAGS, TYPE_S_TAG, TYPE_S_ARGS, TYPE_S_ARG, TYPE_M_ID, TYPE_M_NTAGS, TYPE_M_NARGS, TYPE_M_TAGS, TYPE_M_TAG, TYPE_M_ARGS, TYPE_M_ARG, }; sinsp_filter_check_evtin(); ~sinsp_filter_check_evtin(); int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering); sinsp_filter_check* allocate_new(); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); bool compare(sinsp_evt *evt); uint64_t m_u64val; uint64_t m_tsdelta; uint32_t m_u32val; string m_strstorage; string m_argname; int32_t m_argid; uint32_t m_evtid; uint32_t m_evtid1; const ppm_param_info* m_arginfo; // // Note: this copy of the field is used by some fields, like TYPE_ARGS and // TYPE_RESARG, that need to do on the fly type customization // filtercheck_field_info m_customfield; private: int32_t extract_arg(string fldname, string val); inline uint8_t* extract_tracer(sinsp_evt *evt, sinsp_partial_tracer* pae, OUT uint32_t* len); inline bool compare_tracer(sinsp_evt *evt, sinsp_partial_tracer* pae); bool m_is_compare; char* m_storage; uint32_t m_storage_size; const char* m_cargname; sinsp_filter_check_reference* m_converter; }; // // Fake filter check used by the event formatter to render format text // class rawstring_check : public sinsp_filter_check { public: rawstring_check(string text); sinsp_filter_check* allocate_new(); void set_text(string text); int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); // XXX this is overkill and wasted for most of the fields. // It could be optimized by dynamically allocating the right amount // of memory, but we don't care for the moment since we expect filters // to be pretty small. string m_text; uint32_t m_text_len; }; // // syslog checks // class sinsp_decoder_syslog; class sinsp_filter_check_syslog : public sinsp_filter_check { public: enum check_type { TYPE_FACILITY_STR = 0, TYPE_FACILITY, TYPE_SEVERITY_STR, TYPE_SEVERITY, TYPE_MESSAGE, }; sinsp_filter_check_syslog(); sinsp_filter_check* allocate_new(); int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); sinsp_decoder_syslog* m_decoder; uint32_t m_gid; string m_name; }; class sinsp_filter_check_container : public sinsp_filter_check { public: enum check_type { TYPE_CONTAINER_ID = 0, TYPE_CONTAINER_NAME, TYPE_CONTAINER_IMAGE, TYPE_CONTAINER_IMAGE_ID, TYPE_CONTAINER_TYPE, TYPE_CONTAINER_PRIVILEGED, TYPE_CONTAINER_MOUNTS, TYPE_CONTAINER_MOUNT, TYPE_CONTAINER_MOUNT_SOURCE, TYPE_CONTAINER_MOUNT_DEST, TYPE_CONTAINER_MOUNT_MODE, TYPE_CONTAINER_MOUNT_RDWR, TYPE_CONTAINER_MOUNT_PROPAGATION, TYPE_CONTAINER_IMAGE_REPOSITORY, TYPE_CONTAINER_IMAGE_TAG, TYPE_CONTAINER_IMAGE_DIGEST, TYPE_CONTAINER_HEALTHCHECK, TYPE_CONTAINER_LIVENESS_PROBE, TYPE_CONTAINER_READINESS_PROBE, }; sinsp_filter_check_container(); sinsp_filter_check* allocate_new(); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); private: int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering); int32_t extract_arg(const string& val, size_t basename); string m_tstr; uint32_t m_u32val; int32_t m_argid; string m_argstr; }; // // For internal use // class sinsp_filter_check_reference : public sinsp_filter_check { public: enum alignment { ALIGN_LEFT, ALIGN_RIGHT, }; sinsp_filter_check_reference(); sinsp_filter_check* allocate_new(); inline void set_val(ppm_param_type type, uint8_t* val, int32_t len, uint32_t cnt, ppm_print_format print_format) { m_finfo.m_type = type; m_val = val; m_len = len; m_cnt = cnt; m_print_format = print_format; } int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); char* tostring_nice(sinsp_evt* evt, uint32_t str_len, uint64_t time_delta); Json::Value tojson(sinsp_evt* evt, uint32_t str_len, uint64_t time_delta); private: inline char* format_bytes(double val, uint32_t str_len, bool is_int); inline char* format_time(uint64_t val, uint32_t str_len); char* print_double(uint8_t* rawval, uint32_t str_len); char* print_int(uint8_t* rawval, uint32_t str_len); filtercheck_field_info m_finfo; uint8_t* m_val; uint32_t m_len; double m_cnt; // For averages, this stores the entry count ppm_print_format m_print_format; }; // // For internal use // class sinsp_filter_check_utils : public sinsp_filter_check { public: enum check_type { TYPE_CNT, }; sinsp_filter_check_utils(); sinsp_filter_check* allocate_new(); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); private: uint64_t m_cnt; }; // // fdlist checks // class sinsp_filter_check_fdlist : public sinsp_filter_check { public: enum check_type { TYPE_FDNUMS = 0, TYPE_FDNAMES = 1, TYPE_CLIENTIPS = 2, TYPE_SERVERIPS = 3, TYPE_CLIENTPORTS = 4, TYPE_SERVERPORTS = 5, }; sinsp_filter_check_fdlist(); sinsp_filter_check* allocate_new(); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); private: string m_strval; char m_addrbuff[100]; }; #ifndef CYGWING_AGENT class sinsp_filter_check_k8s : public sinsp_filter_check { public: enum check_type { TYPE_K8S_POD_NAME = 0, TYPE_K8S_POD_ID, TYPE_K8S_POD_LABEL, TYPE_K8S_POD_LABELS, TYPE_K8S_RC_NAME, TYPE_K8S_RC_ID, TYPE_K8S_RC_LABEL, TYPE_K8S_RC_LABELS, TYPE_K8S_SVC_NAME, TYPE_K8S_SVC_ID, TYPE_K8S_SVC_LABEL, TYPE_K8S_SVC_LABELS, TYPE_K8S_NS_NAME, TYPE_K8S_NS_ID, TYPE_K8S_NS_LABEL, TYPE_K8S_NS_LABELS, TYPE_K8S_RS_NAME, TYPE_K8S_RS_ID, TYPE_K8S_RS_LABEL, TYPE_K8S_RS_LABELS, TYPE_K8S_DEPLOYMENT_NAME, TYPE_K8S_DEPLOYMENT_ID, TYPE_K8S_DEPLOYMENT_LABEL, TYPE_K8S_DEPLOYMENT_LABELS, }; sinsp_filter_check_k8s(); sinsp_filter_check* allocate_new(); int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); private: int32_t extract_arg(const string& fldname, const string& val); const k8s_pod_t* find_pod_for_thread(const sinsp_threadinfo* tinfo); const k8s_ns_t* find_ns_by_name(const string& ns_name); const k8s_rc_t* find_rc_by_pod(const k8s_pod_t* pod); const k8s_rs_t* find_rs_by_pod(const k8s_pod_t* pod); vector find_svc_by_pod(const k8s_pod_t* pod); const k8s_deployment_t* find_deployment_by_pod(const k8s_pod_t* pod); void concatenate_labels(const k8s_pair_list& labels, string* s); bool find_label(const k8s_pair_list& labels, const string& key, string* value); string m_argname; string m_tstr; }; #endif // CYGWING_AGENT #ifndef CYGWING_AGENT class sinsp_filter_check_mesos : public sinsp_filter_check { public: enum check_type { TYPE_MESOS_TASK_NAME = 0, TYPE_MESOS_TASK_ID, TYPE_MESOS_TASK_LABEL, TYPE_MESOS_TASK_LABELS, TYPE_MESOS_FRAMEWORK_NAME, TYPE_MESOS_FRAMEWORK_ID, TYPE_MARATHON_APP_NAME, TYPE_MARATHON_APP_ID, TYPE_MARATHON_APP_LABEL, TYPE_MARATHON_APP_LABELS, TYPE_MARATHON_GROUP_NAME, TYPE_MARATHON_GROUP_ID, }; sinsp_filter_check_mesos(); sinsp_filter_check* allocate_new(); int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true); private: int32_t extract_arg(const string& fldname, const string& val); mesos_task::ptr_t find_task_for_thread(const sinsp_threadinfo* tinfo); const mesos_framework* find_framework_by_task(mesos_task::ptr_t task); marathon_app::ptr_t find_app_by_task(mesos_task::ptr_t task); marathon_group::ptr_t find_group_by_task(mesos_task::ptr_t task); void concatenate_labels(const mesos_pair_list& labels, string* s); bool find_label(const mesos_pair_list& labels, const string& key, string* value); string m_argname; string m_tstr; }; #endif // CYGWING_AGENT #endif // HAS_FILTERING sysdig-0.26.4/userspace/libsinsp/gen_filter.cpp000066400000000000000000000077651352731327100215670ustar00rootroot00000000000000/* Copyright (C) 2013-2014 Draios inc. This file is part of sysdig. sysdig is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. sysdig is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with sysdig. If not, see . */ #include #include "stdint.h" #include "gen_filter.h" #include "sinsp.h" #include "sinsp_int.h" gen_event::gen_event() { } gen_event::~gen_event() { } void gen_event::set_check_id(int32_t id) { if (id) { m_check_id = id; } } int32_t gen_event::get_check_id() { return m_check_id; } gen_event_filter_check::gen_event_filter_check() { } gen_event_filter_check::~gen_event_filter_check() { } void gen_event_filter_check::set_check_id(int32_t id) { m_check_id = id; } int32_t gen_event_filter_check::get_check_id() { return m_check_id; } /////////////////////////////////////////////////////////////////////////////// // gen_event_filter_expression implementation /////////////////////////////////////////////////////////////////////////////// gen_event_filter_expression::gen_event_filter_expression() { m_parent = NULL; } gen_event_filter_expression::~gen_event_filter_expression() { uint32_t j; for(j = 0; j < m_checks.size(); j++) { delete m_checks[j]; } } void gen_event_filter_expression::add_check(gen_event_filter_check* chk) { m_checks.push_back(chk); } bool gen_event_filter_expression::compare(gen_event *evt) { uint32_t j; uint32_t size = (uint32_t)m_checks.size(); bool res = true; gen_event_filter_check* chk = NULL; for(j = 0; j < size; j++) { chk = m_checks[j]; ASSERT(chk != NULL); if(j == 0) { switch(chk->m_boolop) { case BO_NONE: res = chk->compare(evt); if (res) { evt->set_check_id(chk->get_check_id()); } break; case BO_NOT: res = !chk->compare(evt); break; default: ASSERT(false); break; } } else { switch(chk->m_boolop) { case BO_OR: if(res) { goto done; } res = chk->compare(evt); if (res) { evt->set_check_id(chk->get_check_id()); } break; case BO_AND: if(!res) { goto done; } res = chk->compare(evt); if (res) { evt->set_check_id(chk->get_check_id()); } break; case BO_ORNOT: if(res) { goto done; } res = !chk->compare(evt); if (res) { evt->set_check_id(chk->get_check_id()); } break; case BO_ANDNOT: if(!res) { goto done; } res = !chk->compare(evt); if (res) { evt->set_check_id(chk->get_check_id()); } break; default: ASSERT(false); break; } } } done: return res; } uint8_t *gen_event_filter_expression::extract(gen_event *evt, uint32_t *len, bool sanitize_strings) { return NULL; } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter implementation /////////////////////////////////////////////////////////////////////////////// gen_event_filter::gen_event_filter() { m_filter = new gen_event_filter_expression(); m_curexpr = m_filter; } gen_event_filter::~gen_event_filter() { if(m_filter) { delete m_filter; } } void gen_event_filter::push_expression(boolop op) { gen_event_filter_expression* newexpr = new gen_event_filter_expression(); newexpr->m_boolop = op; newexpr->m_parent = m_curexpr; add_check((gen_event_filter_check*)newexpr); m_curexpr = newexpr; } void gen_event_filter::pop_expression() { ASSERT(m_curexpr->m_parent != NULL); m_curexpr = m_curexpr->m_parent; } bool gen_event_filter::run(gen_event *evt) { return m_filter->compare(evt); } void gen_event_filter::add_check(gen_event_filter_check* chk) { m_curexpr->add_check((gen_event_filter_check *) chk); } sysdig-0.26.4/userspace/libsinsp/gen_filter.h000066400000000000000000000103061352731327100212150ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios inc. This file is part of sysdig. sysdig is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. sysdig is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with sysdig. If not, see . */ #pragma once #include /* * Operators to compare events */ enum cmpop { CO_NONE = 0, CO_EQ = 1, CO_NE = 2, CO_LT = 3, CO_LE = 4, CO_GT = 5, CO_GE = 6, CO_CONTAINS = 7, CO_IN = 8, CO_EXISTS = 9, CO_ICONTAINS = 10, CO_STARTSWITH = 11, CO_GLOB = 12, CO_PMATCH = 13, CO_ENDSWITH = 14 }; enum boolop { BO_NONE = 0, BO_NOT = 1, BO_OR = 2, BO_AND = 4, // obtained by bitwise OR'ing with one of above ops BO_ORNOT = 3, BO_ANDNOT = 5, }; enum evt_src { ESRC_NONE = 0, ESRC_SINSP = 1, ESRC_K8S_AUDIT = 2, ESRC_MAX = 3, }; class gen_event { public: gen_event(); virtual ~gen_event(); /*! \brief Set an opaque "check id", corresponding to the id of the last filtercheck that matched this event. */ void set_check_id(int32_t id); /*! \brief Get the opaque "check id" (-1 if not set). */ int32_t get_check_id(); // Every event must expose a timestamp virtual uint64_t get_ts() = 0; /*! \brief Get the source of the event. */ virtual uint16_t get_source() = 0; /*! \brief Get the type of the event. */ virtual uint16_t get_type() = 0; private: int32_t m_check_id = 0; }; class gen_event_filter_check { public: gen_event_filter_check(); virtual ~gen_event_filter_check(); boolop m_boolop; cmpop m_cmpop; virtual int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) = 0; virtual void add_filter_value(const char* str, uint32_t len, uint32_t i = 0 ) = 0; virtual bool compare(gen_event *evt) = 0; virtual uint8_t* extract(gen_event *evt, uint32_t* len, bool sanitize_strings = true) = 0; // // Configure numeric id to be set on events that match this filter // void set_check_id(int32_t id); virtual int32_t get_check_id(); private: int32_t m_check_id = 0; }; /////////////////////////////////////////////////////////////////////////////// // Filter expression class // A filter expression contains multiple filters connected by boolean expressions, // e.g. "check or check", "check and check and check", "not check" /////////////////////////////////////////////////////////////////////////////// class gen_event_filter_expression : public gen_event_filter_check { public: gen_event_filter_expression(); virtual ~gen_event_filter_expression(); // // The following methods are part of the filter check interface but are irrelevant // for this class, because they are used only for the leaves of the filtering tree. // int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) { return 0; } void add_filter_value(const char* str, uint32_t len, uint32_t i = 0 ) { return; } void add_check(gen_event_filter_check* chk); bool compare(gen_event *evt); uint8_t* extract(gen_event *evt, uint32_t* len, bool sanitize_strings = true); gen_event_filter_expression* m_parent; std::vector m_checks; }; class gen_event_filter { public: gen_event_filter(); virtual ~gen_event_filter(); /*! \brief Applies the filter to the given event. \param evt Pointer that needs to be filtered. \return true if the event is accepted by the filter, false if it's rejected. */ bool run(gen_event *evt); void push_expression(boolop op); void pop_expression(); void add_check(gen_event_filter_check* chk); protected: gen_event_filter_expression* m_curexpr; gen_event_filter_expression* m_filter; }; class gen_event_filter_factory { public: gen_event_filter_factory() {}; virtual ~gen_event_filter_factory() {}; // Create a new filter virtual gen_event_filter *new_filter() = 0; // Create a new filtercheck virtual gen_event_filter_check *new_filtercheck(const char *fldname) = 0; }; sysdig-0.26.4/userspace/libsinsp/grpc_channel_registry.cpp000066400000000000000000000023071352731327100240070ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "grpc_channel_registry.h" std::map> libsinsp::grpc_channel_registry::s_channels; std::shared_ptr libsinsp::grpc_channel_registry::get_channel(const std::string &url) { auto it = s_channels.find(url); if(it == s_channels.end()) { auto chan = grpc::CreateChannel(url, grpc::InsecureChannelCredentials()); s_channels[url] = chan; return chan; } else { auto chan = it->second.lock(); if(chan == nullptr) { chan = grpc::CreateChannel(url, grpc::InsecureChannelCredentials()); s_channels[url] = chan; } return chan; } } sysdig-0.26.4/userspace/libsinsp/grpc_channel_registry.h000066400000000000000000000017761352731327100234650ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #ifdef GRPC_INCLUDE_IS_GRPCPP # include #else # include #endif namespace libsinsp { class grpc_channel_registry { public: // Return a (shared) grpc::Channel for the provided url. static std::shared_ptr get_channel(const std::string &url); private: static std::map> s_channels; }; } sysdig-0.26.4/userspace/libsinsp/http_parser.c000066400000000000000000002131531352731327100214320ustar00rootroot00000000000000/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev * * Additional changes are licensed under the same terms as NGINX and * copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "http_parser.h" #include #include #include #include #include #include #ifndef ULLONG_MAX # define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ #endif #ifndef MIN # define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif #ifndef ARRAY_SIZE # define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #endif #ifndef BIT_AT # define BIT_AT(a, i) \ (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ (1 << ((unsigned int) (i) & 7)))) #endif #ifndef ELEM_AT # define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) #endif #define SET_ERRNO(e) \ do { \ parser->http_errno = (e); \ } while(0) #define CURRENT_STATE() p_state #define UPDATE_STATE(V) p_state = (enum state) (V); #define RETURN(V) \ do { \ parser->state = CURRENT_STATE(); \ return (V); \ } while (0); #define REEXECUTE() \ goto reexecute; \ #ifdef __GNUC__ # define LIKELY(X) __builtin_expect(!!(X), 1) # define UNLIKELY(X) __builtin_expect(!!(X), 0) #else # define LIKELY(X) (X) # define UNLIKELY(X) (X) #endif /* Run the notify callback FOR, returning ER if it fails */ #define CALLBACK_NOTIFY_(FOR, ER) \ do { \ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ \ if (LIKELY(settings->on_##FOR)) { \ parser->state = CURRENT_STATE(); \ if (UNLIKELY(0 != settings->on_##FOR(parser))) { \ SET_ERRNO(HPE_CB_##FOR); \ } \ UPDATE_STATE(parser->state); \ \ /* We either errored above or got paused; get out */ \ if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ return (ER); \ } \ } \ } while (0) /* Run the notify callback FOR and consume the current byte */ #define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) /* Run the notify callback FOR and don't consume the current byte */ #define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) /* Run data callback FOR with LEN bytes, returning ER if it fails */ #define CALLBACK_DATA_(FOR, LEN, ER) \ do { \ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ \ if (FOR##_mark) { \ if (LIKELY(settings->on_##FOR)) { \ parser->state = CURRENT_STATE(); \ if (UNLIKELY(0 != \ settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \ SET_ERRNO(HPE_CB_##FOR); \ } \ UPDATE_STATE(parser->state); \ \ /* We either errored above or got paused; get out */ \ if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ return (ER); \ } \ } \ FOR##_mark = NULL; \ } \ } while (0) /* Run the data callback FOR and consume the current byte */ #define CALLBACK_DATA(FOR) \ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) /* Run the data callback FOR and don't consume the current byte */ #define CALLBACK_DATA_NOADVANCE(FOR) \ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) /* Set the mark FOR; non-destructive if mark is already set */ #define MARK(FOR) \ do { \ if (!FOR##_mark) { \ FOR##_mark = p; \ } \ } while (0) /* Don't allow the total size of the HTTP headers (including the status * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect * embedders against denial-of-service attacks where the attacker feeds * us a never-ending header that the embedder keeps buffering. * * This check is arguably the responsibility of embedders but we're doing * it on the embedder's behalf because most won't bother and this way we * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger * than any reasonable request or response so this should never affect * day-to-day operation. */ #define COUNT_HEADER_SIZE(V) \ do { \ parser->nread += (V); \ if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \ SET_ERRNO(HPE_HEADER_OVERFLOW); \ goto error; \ } \ } while (0) #define PROXY_CONNECTION "proxy-connection" #define CONNECTION "connection" #define CONTENT_LENGTH "content-length" #define TRANSFER_ENCODING "transfer-encoding" #define UPGRADE "upgrade" #define CHUNKED "chunked" #define KEEP_ALIVE "keep-alive" #define CLOSE "close" static const char *method_strings[] = { #define XX(num, name, string) #string, HTTP_METHOD_MAP(XX) #undef XX }; /* Tokens as defined by rfc 2616. Also lowercases them. * token = 1* * separators = "(" | ")" | "<" | ">" | "@" * | "," | ";" | ":" | "\" | <"> * | "/" | "[" | "]" | "?" | "=" * | "{" | "}" | SP | HT */ static const char tokens[256] = { /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ 0, 0, 0, 0, 0, 0, 0, 0, /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ 0, 0, 0, 0, 0, 0, 0, 0, /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ 0, 0, 0, 0, 0, 0, 0, 0, /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 0, 0, 0, 0, 0, 0, 0, 0, /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ 0, '!', 0, '#', '$', '%', '&', '\'', /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 0, 0, '*', '+', 0, '-', '.', 0, /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ '0', '1', '2', '3', '4', '5', '6', '7', /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ '8', '9', 0, 0, 0, 0, 0, 0, /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ 'x', 'y', 'z', 0, 0, 0, '^', '_', /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ 'x', 'y', 'z', 0, '|', 0, '~', 0 }; static const int8_t unhex[256] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }; #if HTTP_PARSER_STRICT # define T(v) 0 #else # define T(v) v #endif static const uint8_t normal_url_char[32] = { /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; #undef T enum state { s_dead = 1 /* important that this is > 0 */ , s_start_req_or_res , s_res_or_resp_H , s_start_res , s_res_H , s_res_HT , s_res_HTT , s_res_HTTP , s_res_first_http_major , s_res_http_major , s_res_first_http_minor , s_res_http_minor , s_res_first_status_code , s_res_status_code , s_res_status_start , s_res_status , s_res_line_almost_done , s_start_req , s_req_method , s_req_spaces_before_url , s_req_schema , s_req_schema_slash , s_req_schema_slash_slash , s_req_server_start , s_req_server , s_req_server_with_at , s_req_path , s_req_query_string_start , s_req_query_string , s_req_fragment_start , s_req_fragment , s_req_http_start , s_req_http_H , s_req_http_HT , s_req_http_HTT , s_req_http_HTTP , s_req_first_http_major , s_req_http_major , s_req_first_http_minor , s_req_http_minor , s_req_line_almost_done , s_header_field_start , s_header_field , s_header_value_discard_ws , s_header_value_discard_ws_almost_done , s_header_value_discard_lws , s_header_value_start , s_header_value , s_header_value_lws , s_header_almost_done , s_chunk_size_start , s_chunk_size , s_chunk_parameters , s_chunk_size_almost_done , s_headers_almost_done , s_headers_done /* Important: 's_headers_done' must be the last 'header' state. All * states beyond this must be 'body' states. It is used for overflow * checking. See the PARSING_HEADER() macro. */ , s_chunk_data , s_chunk_data_almost_done , s_chunk_data_done , s_body_identity , s_body_identity_eof , s_message_done }; #define PARSING_HEADER(state) (state <= s_headers_done) enum header_states { h_general = 0 , h_C , h_CO , h_CON , h_matching_connection , h_matching_proxy_connection , h_matching_content_length , h_matching_transfer_encoding , h_matching_upgrade , h_connection , h_content_length , h_transfer_encoding , h_upgrade , h_matching_transfer_encoding_chunked , h_matching_connection_token_start , h_matching_connection_keep_alive , h_matching_connection_close , h_matching_connection_upgrade , h_matching_connection_token , h_transfer_encoding_chunked , h_connection_keep_alive , h_connection_close , h_connection_upgrade }; enum http_host_state { s_http_host_dead = 1 , s_http_userinfo_start , s_http_userinfo , s_http_host_start , s_http_host_v6_start , s_http_host , s_http_host_v6 , s_http_host_v6_end , s_http_host_v6_zone_start , s_http_host_v6_zone , s_http_host_port_start , s_http_host_port }; /* Macros for character classes; depends on strict-mode */ #define CR '\r' #define LF '\n' #define LOWER(c) (unsigned char)(c | 0x20) #define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') #define IS_NUM(c) ((c) >= '0' && (c) <= '9') #define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) #define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) #define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ (c) == ')') #define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ (c) == '$' || (c) == ',') #define STRICT_TOKEN(c) (tokens[(unsigned char)c]) #if HTTP_PARSER_STRICT #define TOKEN(c) (tokens[(unsigned char)c]) #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) #define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') #else #define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) #define IS_URL_CHAR(c) \ (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) #define IS_HOST_CHAR(c) \ (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') #endif /** * Verify that a char is a valid visible (printable) US-ASCII * character or %x80-FF **/ #define IS_HEADER_CHAR(ch) \ (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127)) #define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) #if HTTP_PARSER_STRICT # define STRICT_CHECK(cond) \ do { \ if (cond) { \ SET_ERRNO(HPE_STRICT); \ goto error; \ } \ } while (0) # define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) #else # define STRICT_CHECK(cond) # define NEW_MESSAGE() start_state #endif /* Map errno values to strings for human-readable output */ #define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s }, static struct { const char *name; const char *description; } http_strerror_tab[] = { HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) }; #undef HTTP_STRERROR_GEN int http_message_needs_eof(const http_parser *parser); /* Our URL parser. * * This is designed to be shared by http_parser_execute() for URL validation, * hence it has a state transition + byte-for-byte interface. In addition, it * is meant to be embedded in http_parser_parse_url(), which does the dirty * work of turning state transitions URL components for its API. * * This function should only be invoked with non-space characters. It is * assumed that the caller cares about (and can detect) the transition between * URL and non-URL states by looking for these. */ static enum state parse_url_char(enum state s, const char ch) { if (ch == ' ' || ch == '\r' || ch == '\n') { return s_dead; } #if HTTP_PARSER_STRICT if (ch == '\t' || ch == '\f') { return s_dead; } #endif switch (s) { case s_req_spaces_before_url: /* Proxied requests are followed by scheme of an absolute URI (alpha). * All methods except CONNECT are followed by '/' or '*'. */ if (ch == '/' || ch == '*') { return s_req_path; } if (IS_ALPHA(ch)) { return s_req_schema; } break; case s_req_schema: if (IS_ALPHA(ch)) { return s; } if (ch == ':') { return s_req_schema_slash; } break; case s_req_schema_slash: if (ch == '/') { return s_req_schema_slash_slash; } break; case s_req_schema_slash_slash: if (ch == '/') { return s_req_server_start; } break; case s_req_server_with_at: if (ch == '@') { return s_dead; } /* FALLTHROUGH */ case s_req_server_start: case s_req_server: if (ch == '/') { return s_req_path; } if (ch == '?') { return s_req_query_string_start; } if (ch == '@') { return s_req_server_with_at; } if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { return s_req_server; } break; case s_req_path: if (IS_URL_CHAR(ch)) { return s; } switch (ch) { case '?': return s_req_query_string_start; case '#': return s_req_fragment_start; } break; case s_req_query_string_start: case s_req_query_string: if (IS_URL_CHAR(ch)) { return s_req_query_string; } switch (ch) { case '?': /* allow extra '?' in query string */ return s_req_query_string; case '#': return s_req_fragment_start; } break; case s_req_fragment_start: if (IS_URL_CHAR(ch)) { return s_req_fragment; } switch (ch) { case '?': return s_req_fragment; case '#': return s; } break; case s_req_fragment: if (IS_URL_CHAR(ch)) { return s; } switch (ch) { case '?': case '#': return s; } break; default: break; } /* We should never fall out of the switch above unless there's an error */ return s_dead; } size_t http_parser_execute (http_parser *parser, const http_parser_settings *settings, const char *data, size_t len) { char c, ch; int8_t unhex_val; const char *p = data; const char *header_field_mark = 0; const char *header_value_mark = 0; const char *url_mark = 0; const char *body_mark = 0; const char *status_mark = 0; enum state p_state = (enum state) parser->state; const unsigned int lenient = parser->lenient_http_headers; /* We're in an error state. Don't bother doing anything. */ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { return 0; } if (len == 0) { switch (CURRENT_STATE()) { case s_body_identity_eof: /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if * we got paused. */ CALLBACK_NOTIFY_NOADVANCE(message_complete); return 0; case s_dead: case s_start_req_or_res: case s_start_res: case s_start_req: return 0; default: SET_ERRNO(HPE_INVALID_EOF_STATE); return 1; } } if (CURRENT_STATE() == s_header_field) header_field_mark = data; if (CURRENT_STATE() == s_header_value) header_value_mark = data; switch (CURRENT_STATE()) { case s_req_path: case s_req_schema: case s_req_schema_slash: case s_req_schema_slash_slash: case s_req_server_start: case s_req_server: case s_req_server_with_at: case s_req_query_string_start: case s_req_query_string: case s_req_fragment_start: case s_req_fragment: url_mark = data; break; case s_res_status: status_mark = data; break; default: break; } for (p=data; p != data + len; p++) { ch = *p; if (PARSING_HEADER(CURRENT_STATE())) COUNT_HEADER_SIZE(1); reexecute: switch (CURRENT_STATE()) { case s_dead: /* this state is used after a 'Connection: close' message * the parser will error out if it reads another message */ if (LIKELY(ch == CR || ch == LF)) break; SET_ERRNO(HPE_CLOSED_CONNECTION); goto error; case s_start_req_or_res: { if (ch == CR || ch == LF) break; parser->flags = 0; parser->content_length = ULLONG_MAX; if (ch == 'H') { UPDATE_STATE(s_res_or_resp_H); CALLBACK_NOTIFY(message_begin); } else { parser->type = HTTP_REQUEST; UPDATE_STATE(s_start_req); REEXECUTE(); } break; } case s_res_or_resp_H: if (ch == 'T') { parser->type = HTTP_RESPONSE; UPDATE_STATE(s_res_HT); } else { if (UNLIKELY(ch != 'E')) { SET_ERRNO(HPE_INVALID_CONSTANT); goto error; } parser->type = HTTP_REQUEST; parser->method = HTTP_HEAD; parser->index = 2; UPDATE_STATE(s_req_method); } break; case s_start_res: { parser->flags = 0; parser->content_length = ULLONG_MAX; switch (ch) { case 'H': UPDATE_STATE(s_res_H); break; case CR: case LF: break; default: SET_ERRNO(HPE_INVALID_CONSTANT); goto error; } CALLBACK_NOTIFY(message_begin); break; } case s_res_H: STRICT_CHECK(ch != 'T'); UPDATE_STATE(s_res_HT); break; case s_res_HT: STRICT_CHECK(ch != 'T'); UPDATE_STATE(s_res_HTT); break; case s_res_HTT: STRICT_CHECK(ch != 'P'); UPDATE_STATE(s_res_HTTP); break; case s_res_HTTP: STRICT_CHECK(ch != '/'); UPDATE_STATE(s_res_first_http_major); break; case s_res_first_http_major: if (UNLIKELY(ch < '0' || ch > '9')) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major = ch - '0'; UPDATE_STATE(s_res_http_major); break; /* major HTTP version or dot */ case s_res_http_major: { if (ch == '.') { UPDATE_STATE(s_res_first_http_minor); break; } if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major *= 10; parser->http_major += ch - '0'; if (UNLIKELY(parser->http_major > 999)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } break; } /* first digit of minor HTTP version */ case s_res_first_http_minor: if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor = ch - '0'; UPDATE_STATE(s_res_http_minor); break; /* minor HTTP version or end of request line */ case s_res_http_minor: { if (ch == ' ') { UPDATE_STATE(s_res_first_status_code); break; } if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor *= 10; parser->http_minor += ch - '0'; if (UNLIKELY(parser->http_minor > 999)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } break; } case s_res_first_status_code: { if (!IS_NUM(ch)) { if (ch == ' ') { break; } SET_ERRNO(HPE_INVALID_STATUS); goto error; } parser->status_code = ch - '0'; UPDATE_STATE(s_res_status_code); break; } case s_res_status_code: { if (!IS_NUM(ch)) { switch (ch) { case ' ': UPDATE_STATE(s_res_status_start); break; case CR: UPDATE_STATE(s_res_line_almost_done); break; case LF: UPDATE_STATE(s_header_field_start); break; default: SET_ERRNO(HPE_INVALID_STATUS); goto error; } break; } parser->status_code *= 10; parser->status_code += ch - '0'; if (UNLIKELY(parser->status_code > 999)) { SET_ERRNO(HPE_INVALID_STATUS); goto error; } break; } case s_res_status_start: { if (ch == CR) { UPDATE_STATE(s_res_line_almost_done); break; } if (ch == LF) { UPDATE_STATE(s_header_field_start); break; } MARK(status); UPDATE_STATE(s_res_status); parser->index = 0; break; } case s_res_status: if (ch == CR) { UPDATE_STATE(s_res_line_almost_done); CALLBACK_DATA(status); break; } if (ch == LF) { UPDATE_STATE(s_header_field_start); CALLBACK_DATA(status); break; } break; case s_res_line_almost_done: STRICT_CHECK(ch != LF); UPDATE_STATE(s_header_field_start); break; case s_start_req: { if (ch == CR || ch == LF) break; parser->flags = 0; parser->content_length = ULLONG_MAX; if (UNLIKELY(!IS_ALPHA(ch))) { SET_ERRNO(HPE_INVALID_METHOD); goto error; } parser->method = (enum http_method) 0; parser->index = 1; switch (ch) { case 'A': parser->method = HTTP_ACL; break; case 'B': parser->method = HTTP_BIND; break; case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; case 'D': parser->method = HTTP_DELETE; break; case 'G': parser->method = HTTP_GET; break; case 'H': parser->method = HTTP_HEAD; break; case 'L': parser->method = HTTP_LOCK; /* or LINK */ break; case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; case 'N': parser->method = HTTP_NOTIFY; break; case 'O': parser->method = HTTP_OPTIONS; break; case 'P': parser->method = HTTP_POST; /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ break; case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break; case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break; case 'T': parser->method = HTTP_TRACE; break; case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break; default: SET_ERRNO(HPE_INVALID_METHOD); goto error; } UPDATE_STATE(s_req_method); CALLBACK_NOTIFY(message_begin); break; } case s_req_method: { const char *matcher; if (UNLIKELY(ch == '\0')) { SET_ERRNO(HPE_INVALID_METHOD); goto error; } matcher = method_strings[parser->method]; if (ch == ' ' && matcher[parser->index] == '\0') { UPDATE_STATE(s_req_spaces_before_url); } else if (ch == matcher[parser->index]) { ; /* nada */ } else if (IS_ALPHA(ch)) { switch (parser->method << 16 | parser->index << 8 | ch) { #define XX(meth, pos, ch, new_meth) \ case (HTTP_##meth << 16 | pos << 8 | ch): \ parser->method = HTTP_##new_meth; break; XX(POST, 1, 'U', PUT) XX(POST, 1, 'A', PATCH) XX(CONNECT, 1, 'H', CHECKOUT) XX(CONNECT, 2, 'P', COPY) XX(MKCOL, 1, 'O', MOVE) XX(MKCOL, 1, 'E', MERGE) XX(MKCOL, 2, 'A', MKACTIVITY) XX(MKCOL, 3, 'A', MKCALENDAR) XX(SUBSCRIBE, 1, 'E', SEARCH) XX(REPORT, 2, 'B', REBIND) XX(POST, 1, 'R', PROPFIND) XX(PROPFIND, 4, 'P', PROPPATCH) XX(PUT, 2, 'R', PURGE) XX(LOCK, 1, 'I', LINK) XX(UNLOCK, 2, 'S', UNSUBSCRIBE) XX(UNLOCK, 2, 'B', UNBIND) XX(UNLOCK, 3, 'I', UNLINK) #undef XX default: SET_ERRNO(HPE_INVALID_METHOD); goto error; } } else if (ch == '-' && parser->index == 1 && parser->method == HTTP_MKCOL) { parser->method = HTTP_MSEARCH; } else { SET_ERRNO(HPE_INVALID_METHOD); goto error; } ++parser->index; break; } case s_req_spaces_before_url: { if (ch == ' ') break; MARK(url); if (parser->method == HTTP_CONNECT) { UPDATE_STATE(s_req_server_start); } UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); if (UNLIKELY(CURRENT_STATE() == s_dead)) { SET_ERRNO(HPE_INVALID_URL); goto error; } break; } case s_req_schema: case s_req_schema_slash: case s_req_schema_slash_slash: case s_req_server_start: { switch (ch) { /* No whitespace allowed here */ case ' ': case CR: case LF: SET_ERRNO(HPE_INVALID_URL); goto error; default: UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); if (UNLIKELY(CURRENT_STATE() == s_dead)) { SET_ERRNO(HPE_INVALID_URL); goto error; } } break; } case s_req_server: case s_req_server_with_at: case s_req_path: case s_req_query_string_start: case s_req_query_string: case s_req_fragment_start: case s_req_fragment: { switch (ch) { case ' ': UPDATE_STATE(s_req_http_start); CALLBACK_DATA(url); break; case CR: case LF: parser->http_major = 0; parser->http_minor = 9; UPDATE_STATE((ch == CR) ? s_req_line_almost_done : s_header_field_start); CALLBACK_DATA(url); break; default: UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); if (UNLIKELY(CURRENT_STATE() == s_dead)) { SET_ERRNO(HPE_INVALID_URL); goto error; } } break; } case s_req_http_start: switch (ch) { case 'H': UPDATE_STATE(s_req_http_H); break; case ' ': break; default: SET_ERRNO(HPE_INVALID_CONSTANT); goto error; } break; case s_req_http_H: STRICT_CHECK(ch != 'T'); UPDATE_STATE(s_req_http_HT); break; case s_req_http_HT: STRICT_CHECK(ch != 'T'); UPDATE_STATE(s_req_http_HTT); break; case s_req_http_HTT: STRICT_CHECK(ch != 'P'); UPDATE_STATE(s_req_http_HTTP); break; case s_req_http_HTTP: STRICT_CHECK(ch != '/'); UPDATE_STATE(s_req_first_http_major); break; /* first digit of major HTTP version */ case s_req_first_http_major: if (UNLIKELY(ch < '1' || ch > '9')) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major = ch - '0'; UPDATE_STATE(s_req_http_major); break; /* major HTTP version or dot */ case s_req_http_major: { if (ch == '.') { UPDATE_STATE(s_req_first_http_minor); break; } if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major *= 10; parser->http_major += ch - '0'; if (UNLIKELY(parser->http_major > 999)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } break; } /* first digit of minor HTTP version */ case s_req_first_http_minor: if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor = ch - '0'; UPDATE_STATE(s_req_http_minor); break; /* minor HTTP version or end of request line */ case s_req_http_minor: { if (ch == CR) { UPDATE_STATE(s_req_line_almost_done); break; } if (ch == LF) { UPDATE_STATE(s_header_field_start); break; } /* XXX allow spaces after digit? */ if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor *= 10; parser->http_minor += ch - '0'; if (UNLIKELY(parser->http_minor > 999)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } break; } /* end of request line */ case s_req_line_almost_done: { if (UNLIKELY(ch != LF)) { SET_ERRNO(HPE_LF_EXPECTED); goto error; } UPDATE_STATE(s_header_field_start); break; } case s_header_field_start: { if (ch == CR) { UPDATE_STATE(s_headers_almost_done); break; } if (ch == LF) { /* they might be just sending \n instead of \r\n so this would be * the second \n to denote the end of headers*/ UPDATE_STATE(s_headers_almost_done); REEXECUTE(); } c = TOKEN(ch); if (UNLIKELY(!c)) { SET_ERRNO(HPE_INVALID_HEADER_TOKEN); goto error; } MARK(header_field); parser->index = 0; UPDATE_STATE(s_header_field); switch (c) { case 'c': parser->header_state = h_C; break; case 'p': parser->header_state = h_matching_proxy_connection; break; case 't': parser->header_state = h_matching_transfer_encoding; break; case 'u': parser->header_state = h_matching_upgrade; break; default: parser->header_state = h_general; break; } break; } case s_header_field: { const char* start = p; for (; p != data + len; p++) { ch = *p; c = TOKEN(ch); if (!c) break; switch (parser->header_state) { case h_general: break; case h_C: parser->index++; parser->header_state = (c == 'o' ? h_CO : h_general); break; case h_CO: parser->index++; parser->header_state = (c == 'n' ? h_CON : h_general); break; case h_CON: parser->index++; switch (c) { case 'n': parser->header_state = h_matching_connection; break; case 't': parser->header_state = h_matching_content_length; break; default: parser->header_state = h_general; break; } break; /* connection */ case h_matching_connection: parser->index++; if (parser->index > sizeof(CONNECTION)-1 || c != CONNECTION[parser->index]) { parser->header_state = h_general; } else if (parser->index == sizeof(CONNECTION)-2) { parser->header_state = h_connection; } break; /* proxy-connection */ case h_matching_proxy_connection: parser->index++; if (parser->index > sizeof(PROXY_CONNECTION)-1 || c != PROXY_CONNECTION[parser->index]) { parser->header_state = h_general; } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { parser->header_state = h_connection; } break; /* content-length */ case h_matching_content_length: parser->index++; if (parser->index > sizeof(CONTENT_LENGTH)-1 || c != CONTENT_LENGTH[parser->index]) { parser->header_state = h_general; } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { parser->header_state = h_content_length; } break; /* transfer-encoding */ case h_matching_transfer_encoding: parser->index++; if (parser->index > sizeof(TRANSFER_ENCODING)-1 || c != TRANSFER_ENCODING[parser->index]) { parser->header_state = h_general; } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { parser->header_state = h_transfer_encoding; } break; /* upgrade */ case h_matching_upgrade: parser->index++; if (parser->index > sizeof(UPGRADE)-1 || c != UPGRADE[parser->index]) { parser->header_state = h_general; } else if (parser->index == sizeof(UPGRADE)-2) { parser->header_state = h_upgrade; } break; case h_connection: case h_content_length: case h_transfer_encoding: case h_upgrade: if (ch != ' ') parser->header_state = h_general; break; default: assert(0 && "Unknown header_state"); break; } } COUNT_HEADER_SIZE(p - start); if (p == data + len) { --p; break; } if (ch == ':') { UPDATE_STATE(s_header_value_discard_ws); CALLBACK_DATA(header_field); break; } SET_ERRNO(HPE_INVALID_HEADER_TOKEN); goto error; } case s_header_value_discard_ws: if (ch == ' ' || ch == '\t') break; if (ch == CR) { UPDATE_STATE(s_header_value_discard_ws_almost_done); break; } if (ch == LF) { UPDATE_STATE(s_header_value_discard_lws); break; } /* FALLTHROUGH */ case s_header_value_start: { MARK(header_value); UPDATE_STATE(s_header_value); parser->index = 0; c = LOWER(ch); switch (parser->header_state) { case h_upgrade: parser->flags |= F_UPGRADE; parser->header_state = h_general; break; case h_transfer_encoding: /* looking for 'Transfer-Encoding: chunked' */ if ('c' == c) { parser->header_state = h_matching_transfer_encoding_chunked; } else { parser->header_state = h_general; } break; case h_content_length: if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); goto error; } if (parser->flags & F_CONTENTLENGTH) { SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); goto error; } parser->flags |= F_CONTENTLENGTH; parser->content_length = ch - '0'; break; case h_connection: /* looking for 'Connection: keep-alive' */ if (c == 'k') { parser->header_state = h_matching_connection_keep_alive; /* looking for 'Connection: close' */ } else if (c == 'c') { parser->header_state = h_matching_connection_close; } else if (c == 'u') { parser->header_state = h_matching_connection_upgrade; } else { parser->header_state = h_matching_connection_token; } break; /* Multi-value `Connection` header */ case h_matching_connection_token_start: break; default: parser->header_state = h_general; break; } break; } case s_header_value: { const char* start = p; enum header_states h_state = (enum header_states) parser->header_state; for (; p != data + len; p++) { ch = *p; if (ch == CR) { UPDATE_STATE(s_header_almost_done); parser->header_state = h_state; CALLBACK_DATA(header_value); break; } if (ch == LF) { UPDATE_STATE(s_header_almost_done); COUNT_HEADER_SIZE(p - start); parser->header_state = h_state; CALLBACK_DATA_NOADVANCE(header_value); REEXECUTE(); } if (!lenient && !IS_HEADER_CHAR(ch)) { SET_ERRNO(HPE_INVALID_HEADER_TOKEN); goto error; } c = LOWER(ch); switch (h_state) { case h_general: { const char* p_cr; const char* p_lf; size_t limit = data + len - p; limit = MIN(limit, HTTP_MAX_HEADER_SIZE); p_cr = (const char*) memchr(p, CR, limit); p_lf = (const char*) memchr(p, LF, limit); if (p_cr != NULL) { if (p_lf != NULL && p_cr >= p_lf) p = p_lf; else p = p_cr; } else if (UNLIKELY(p_lf != NULL)) { p = p_lf; } else { p = data + len; } --p; break; } case h_connection: case h_transfer_encoding: assert(0 && "Shouldn't get here."); break; case h_content_length: { uint64_t t; if (ch == ' ') break; if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); parser->header_state = h_state; goto error; } t = parser->content_length; t *= 10; t += ch - '0'; /* Overflow? Test against a conservative limit for simplicity. */ if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); parser->header_state = h_state; goto error; } parser->content_length = t; break; } /* Transfer-Encoding: chunked */ case h_matching_transfer_encoding_chunked: parser->index++; if (parser->index > sizeof(CHUNKED)-1 || c != CHUNKED[parser->index]) { h_state = h_general; } else if (parser->index == sizeof(CHUNKED)-2) { h_state = h_transfer_encoding_chunked; } break; case h_matching_connection_token_start: /* looking for 'Connection: keep-alive' */ if (c == 'k') { h_state = h_matching_connection_keep_alive; /* looking for 'Connection: close' */ } else if (c == 'c') { h_state = h_matching_connection_close; } else if (c == 'u') { h_state = h_matching_connection_upgrade; } else if (STRICT_TOKEN(c)) { h_state = h_matching_connection_token; } else if (c == ' ' || c == '\t') { /* Skip lws */ } else { h_state = h_general; } break; /* looking for 'Connection: keep-alive' */ case h_matching_connection_keep_alive: parser->index++; if (parser->index > sizeof(KEEP_ALIVE)-1 || c != KEEP_ALIVE[parser->index]) { h_state = h_matching_connection_token; } else if (parser->index == sizeof(KEEP_ALIVE)-2) { h_state = h_connection_keep_alive; } break; /* looking for 'Connection: close' */ case h_matching_connection_close: parser->index++; if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { h_state = h_matching_connection_token; } else if (parser->index == sizeof(CLOSE)-2) { h_state = h_connection_close; } break; /* looking for 'Connection: upgrade' */ case h_matching_connection_upgrade: parser->index++; if (parser->index > sizeof(UPGRADE) - 1 || c != UPGRADE[parser->index]) { h_state = h_matching_connection_token; } else if (parser->index == sizeof(UPGRADE)-2) { h_state = h_connection_upgrade; } break; case h_matching_connection_token: if (ch == ',') { h_state = h_matching_connection_token_start; parser->index = 0; } break; case h_transfer_encoding_chunked: if (ch != ' ') h_state = h_general; break; case h_connection_keep_alive: case h_connection_close: case h_connection_upgrade: if (ch == ',') { if (h_state == h_connection_keep_alive) { parser->flags |= F_CONNECTION_KEEP_ALIVE; } else if (h_state == h_connection_close) { parser->flags |= F_CONNECTION_CLOSE; } else if (h_state == h_connection_upgrade) { parser->flags |= F_CONNECTION_UPGRADE; } h_state = h_matching_connection_token_start; parser->index = 0; } else if (ch != ' ') { h_state = h_matching_connection_token; } break; default: UPDATE_STATE(s_header_value); h_state = h_general; break; } } parser->header_state = h_state; COUNT_HEADER_SIZE(p - start); if (p == data + len) --p; break; } case s_header_almost_done: { if (UNLIKELY(ch != LF)) { SET_ERRNO(HPE_LF_EXPECTED); goto error; } UPDATE_STATE(s_header_value_lws); break; } case s_header_value_lws: { if (ch == ' ' || ch == '\t') { UPDATE_STATE(s_header_value_start); REEXECUTE(); } /* finished the header */ switch (parser->header_state) { case h_connection_keep_alive: parser->flags |= F_CONNECTION_KEEP_ALIVE; break; case h_connection_close: parser->flags |= F_CONNECTION_CLOSE; break; case h_transfer_encoding_chunked: parser->flags |= F_CHUNKED; break; case h_connection_upgrade: parser->flags |= F_CONNECTION_UPGRADE; break; default: break; } UPDATE_STATE(s_header_field_start); REEXECUTE(); } case s_header_value_discard_ws_almost_done: { STRICT_CHECK(ch != LF); UPDATE_STATE(s_header_value_discard_lws); break; } case s_header_value_discard_lws: { if (ch == ' ' || ch == '\t') { UPDATE_STATE(s_header_value_discard_ws); break; } else { switch (parser->header_state) { case h_connection_keep_alive: parser->flags |= F_CONNECTION_KEEP_ALIVE; break; case h_connection_close: parser->flags |= F_CONNECTION_CLOSE; break; case h_connection_upgrade: parser->flags |= F_CONNECTION_UPGRADE; break; case h_transfer_encoding_chunked: parser->flags |= F_CHUNKED; break; default: break; } /* header value was empty */ MARK(header_value); UPDATE_STATE(s_header_field_start); CALLBACK_DATA_NOADVANCE(header_value); REEXECUTE(); } } case s_headers_almost_done: { STRICT_CHECK(ch != LF); if (parser->flags & F_TRAILING) { /* End of a chunked request */ UPDATE_STATE(s_message_done); CALLBACK_NOTIFY_NOADVANCE(chunk_complete); REEXECUTE(); } /* Cannot use chunked encoding and a content-length header together per the HTTP specification. */ if ((parser->flags & F_CHUNKED) && (parser->flags & F_CONTENTLENGTH)) { SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); goto error; } UPDATE_STATE(s_headers_done); /* Set this here so that on_headers_complete() callbacks can see it */ parser->upgrade = ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) == (F_UPGRADE | F_CONNECTION_UPGRADE) || parser->method == HTTP_CONNECT); /* Here we call the headers_complete callback. This is somewhat * different than other callbacks because if the user returns 1, we * will interpret that as saying that this message has no body. This * is needed for the annoying case of receiving a response to a HEAD * request. * * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so * we have to simulate it by handling a change in errno below. */ if (settings->on_headers_complete) { switch (settings->on_headers_complete(parser)) { case 0: break; case 2: parser->upgrade = 1; case 1: parser->flags |= F_SKIPBODY; break; default: SET_ERRNO(HPE_CB_headers_complete); RETURN(p - data); /* Error */ } } if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { RETURN(p - data); } REEXECUTE(); } case s_headers_done: { int hasBody; STRICT_CHECK(ch != LF); parser->nread = 0; hasBody = parser->flags & F_CHUNKED || (parser->content_length > 0 && parser->content_length != ULLONG_MAX); if (parser->upgrade && (parser->method == HTTP_CONNECT || (parser->flags & F_SKIPBODY) || !hasBody)) { /* Exit, the rest of the message is in a different protocol. */ UPDATE_STATE(NEW_MESSAGE()); CALLBACK_NOTIFY(message_complete); RETURN((p - data) + 1); } if (parser->flags & F_SKIPBODY) { UPDATE_STATE(NEW_MESSAGE()); CALLBACK_NOTIFY(message_complete); } else if (parser->flags & F_CHUNKED) { /* chunked encoding - ignore Content-Length header */ UPDATE_STATE(s_chunk_size_start); } else { if (parser->content_length == 0) { /* Content-Length header given but zero: Content-Length: 0\r\n */ UPDATE_STATE(NEW_MESSAGE()); CALLBACK_NOTIFY(message_complete); } else if (parser->content_length != ULLONG_MAX) { /* Content-Length header given and non-zero */ UPDATE_STATE(s_body_identity); } else { if (!http_message_needs_eof(parser)) { /* Assume content-length 0 - read the next */ UPDATE_STATE(NEW_MESSAGE()); CALLBACK_NOTIFY(message_complete); } else { /* Read body until EOF */ UPDATE_STATE(s_body_identity_eof); } } } break; } case s_body_identity: { uint64_t to_read = MIN(parser->content_length, (uint64_t) ((data + len) - p)); assert(parser->content_length != 0 && parser->content_length != ULLONG_MAX); /* The difference between advancing content_length and p is because * the latter will automatically advance on the next loop iteration. * Further, if content_length ends up at 0, we want to see the last * byte again for our message complete callback. */ MARK(body); parser->content_length -= to_read; p += to_read - 1; if (parser->content_length == 0) { UPDATE_STATE(s_message_done); /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. * * The alternative to doing this is to wait for the next byte to * trigger the data callback, just as in every other case. The * problem with this is that this makes it difficult for the test * harness to distinguish between complete-on-EOF and * complete-on-length. It's not clear that this distinction is * important for applications, but let's keep it for now. */ CALLBACK_DATA_(body, p - body_mark + 1, p - data); REEXECUTE(); } break; } /* read until EOF */ case s_body_identity_eof: MARK(body); p = data + len - 1; break; case s_message_done: UPDATE_STATE(NEW_MESSAGE()); CALLBACK_NOTIFY(message_complete); if (parser->upgrade) { /* Exit, the rest of the message is in a different protocol. */ RETURN((p - data) + 1); } break; case s_chunk_size_start: { assert(parser->nread == 1); assert(parser->flags & F_CHUNKED); unhex_val = unhex[(unsigned char)ch]; if (UNLIKELY(unhex_val == -1)) { SET_ERRNO(HPE_INVALID_CHUNK_SIZE); goto error; } parser->content_length = unhex_val; UPDATE_STATE(s_chunk_size); break; } case s_chunk_size: { uint64_t t; assert(parser->flags & F_CHUNKED); if (ch == CR) { UPDATE_STATE(s_chunk_size_almost_done); break; } unhex_val = unhex[(unsigned char)ch]; if (unhex_val == -1) { if (ch == ';' || ch == ' ') { UPDATE_STATE(s_chunk_parameters); break; } SET_ERRNO(HPE_INVALID_CHUNK_SIZE); goto error; } t = parser->content_length; t *= 16; t += unhex_val; /* Overflow? Test against a conservative limit for simplicity. */ if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); goto error; } parser->content_length = t; break; } case s_chunk_parameters: { assert(parser->flags & F_CHUNKED); /* just ignore this shit. TODO check for overflow */ if (ch == CR) { UPDATE_STATE(s_chunk_size_almost_done); break; } break; } case s_chunk_size_almost_done: { assert(parser->flags & F_CHUNKED); STRICT_CHECK(ch != LF); parser->nread = 0; if (parser->content_length == 0) { parser->flags |= F_TRAILING; UPDATE_STATE(s_header_field_start); } else { UPDATE_STATE(s_chunk_data); } CALLBACK_NOTIFY(chunk_header); break; } case s_chunk_data: { uint64_t to_read = MIN(parser->content_length, (uint64_t) ((data + len) - p)); assert(parser->flags & F_CHUNKED); assert(parser->content_length != 0 && parser->content_length != ULLONG_MAX); /* See the explanation in s_body_identity for why the content * length and data pointers are managed this way. */ MARK(body); parser->content_length -= to_read; p += to_read - 1; if (parser->content_length == 0) { UPDATE_STATE(s_chunk_data_almost_done); } break; } case s_chunk_data_almost_done: assert(parser->flags & F_CHUNKED); assert(parser->content_length == 0); STRICT_CHECK(ch != CR); UPDATE_STATE(s_chunk_data_done); CALLBACK_DATA(body); break; case s_chunk_data_done: assert(parser->flags & F_CHUNKED); STRICT_CHECK(ch != LF); parser->nread = 0; UPDATE_STATE(s_chunk_size_start); CALLBACK_NOTIFY(chunk_complete); break; default: assert(0 && "unhandled state"); SET_ERRNO(HPE_INVALID_INTERNAL_STATE); goto error; } } /* Run callbacks for any marks that we have leftover after we ran our of * bytes. There should be at most one of these set, so it's OK to invoke * them in series (unset marks will not result in callbacks). * * We use the NOADVANCE() variety of callbacks here because 'p' has already * overflowed 'data' and this allows us to correct for the off-by-one that * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' * value that's in-bounds). */ assert(((header_field_mark ? 1 : 0) + (header_value_mark ? 1 : 0) + (url_mark ? 1 : 0) + (body_mark ? 1 : 0) + (status_mark ? 1 : 0)) <= 1); CALLBACK_DATA_NOADVANCE(header_field); CALLBACK_DATA_NOADVANCE(header_value); CALLBACK_DATA_NOADVANCE(url); CALLBACK_DATA_NOADVANCE(body); CALLBACK_DATA_NOADVANCE(status); RETURN(len); error: if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { SET_ERRNO(HPE_UNKNOWN); } RETURN(p - data); } /* Does the parser need to see an EOF to find the end of the message? */ int http_message_needs_eof (const http_parser *parser) { if (parser->type == HTTP_REQUEST) { return 0; } /* See RFC 2616 section 4.4 */ if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ parser->status_code == 204 || /* No Content */ parser->status_code == 304 || /* Not Modified */ parser->flags & F_SKIPBODY) { /* response to a HEAD request */ return 0; } if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { return 0; } return 1; } int http_should_keep_alive (const http_parser *parser) { if (parser->http_major > 0 && parser->http_minor > 0) { /* HTTP/1.1 */ if (parser->flags & F_CONNECTION_CLOSE) { return 0; } } else { /* HTTP/1.0 or earlier */ if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { return 0; } } return !http_message_needs_eof(parser); } const char * http_method_str (enum http_method m) { return ELEM_AT(method_strings, m, ""); } void http_parser_init (http_parser *parser, enum http_parser_type t) { void *data = parser->data; /* preserve application data */ memset(parser, 0, sizeof(*parser)); parser->data = data; parser->type = t; parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); parser->http_errno = HPE_OK; } void http_parser_settings_init(http_parser_settings *settings) { memset(settings, 0, sizeof(*settings)); } const char * http_errno_name(enum http_errno err) { assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab)); return http_strerror_tab[err].name; } const char * http_errno_description(enum http_errno err) { assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab)); return http_strerror_tab[err].description; } static enum http_host_state http_parse_host_char(enum http_host_state s, const char ch) { switch(s) { case s_http_userinfo: case s_http_userinfo_start: if (ch == '@') { return s_http_host_start; } if (IS_USERINFO_CHAR(ch)) { return s_http_userinfo; } break; case s_http_host_start: if (ch == '[') { return s_http_host_v6_start; } if (IS_HOST_CHAR(ch)) { return s_http_host; } break; case s_http_host: if (IS_HOST_CHAR(ch)) { return s_http_host; } /* FALLTHROUGH */ case s_http_host_v6_end: if (ch == ':') { return s_http_host_port_start; } break; case s_http_host_v6: if (ch == ']') { return s_http_host_v6_end; } /* FALLTHROUGH */ case s_http_host_v6_start: if (IS_HEX(ch) || ch == ':' || ch == '.') { return s_http_host_v6; } if (s == s_http_host_v6 && ch == '%') { return s_http_host_v6_zone_start; } break; case s_http_host_v6_zone: if (ch == ']') { return s_http_host_v6_end; } /* FALLTHROUGH */ case s_http_host_v6_zone_start: /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */ if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' || ch == '~') { return s_http_host_v6_zone; } break; case s_http_host_port: case s_http_host_port_start: if (IS_NUM(ch)) { return s_http_host_port; } break; default: break; } return s_http_host_dead; } static int http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { enum http_host_state s; const char *p; size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; assert(u->field_set & (1 << UF_HOST)); u->field_data[UF_HOST].len = 0; s = found_at ? s_http_userinfo_start : s_http_host_start; for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { enum http_host_state new_s = http_parse_host_char(s, *p); if (new_s == s_http_host_dead) { return 1; } switch(new_s) { case s_http_host: if (s != s_http_host) { u->field_data[UF_HOST].off = p - buf; } u->field_data[UF_HOST].len++; break; case s_http_host_v6: if (s != s_http_host_v6) { u->field_data[UF_HOST].off = p - buf; } u->field_data[UF_HOST].len++; break; case s_http_host_v6_zone_start: case s_http_host_v6_zone: u->field_data[UF_HOST].len++; break; case s_http_host_port: if (s != s_http_host_port) { u->field_data[UF_PORT].off = p - buf; u->field_data[UF_PORT].len = 0; u->field_set |= (1 << UF_PORT); } u->field_data[UF_PORT].len++; break; case s_http_userinfo: if (s != s_http_userinfo) { u->field_data[UF_USERINFO].off = p - buf ; u->field_data[UF_USERINFO].len = 0; u->field_set |= (1 << UF_USERINFO); } u->field_data[UF_USERINFO].len++; break; default: break; } s = new_s; } /* Make sure we don't end somewhere unexpected */ switch (s) { case s_http_host_start: case s_http_host_v6_start: case s_http_host_v6: case s_http_host_v6_zone_start: case s_http_host_v6_zone: case s_http_host_port_start: case s_http_userinfo: case s_http_userinfo_start: return 1; default: break; } return 0; } void http_parser_url_init(struct http_parser_url *u) { memset(u, 0, sizeof(*u)); } int http_parser_parse_url(const char *buf, size_t buflen, int is_connect, struct http_parser_url *u) { enum state s; const char *p; enum http_parser_url_fields uf, old_uf; int found_at = 0; u->port = u->field_set = 0; s = is_connect ? s_req_server_start : s_req_spaces_before_url; old_uf = UF_MAX; for (p = buf; p < buf + buflen; p++) { s = parse_url_char(s, *p); /* Figure out the next field that we're operating on */ switch (s) { case s_dead: return 1; /* Skip delimiters */ case s_req_schema_slash: case s_req_schema_slash_slash: case s_req_server_start: case s_req_query_string_start: case s_req_fragment_start: continue; case s_req_schema: uf = UF_SCHEMA; break; case s_req_server_with_at: found_at = 1; /* FALLTROUGH */ case s_req_server: uf = UF_HOST; break; case s_req_path: uf = UF_PATH; break; case s_req_query_string: uf = UF_QUERY; break; case s_req_fragment: uf = UF_FRAGMENT; break; default: assert(!"Unexpected state"); return 1; } /* Nothing's changed; soldier on */ if (uf == old_uf) { u->field_data[uf].len++; continue; } u->field_data[uf].off = p - buf; u->field_data[uf].len = 1; u->field_set |= (1 << uf); old_uf = uf; } /* host must be present if there is a schema */ /* parsing http:///toto will fail */ if ((u->field_set & (1 << UF_SCHEMA)) && (u->field_set & (1 << UF_HOST)) == 0) { return 1; } if (u->field_set & (1 << UF_HOST)) { if (http_parse_host(buf, u, found_at) != 0) { return 1; } } /* CONNECT requests can only contain "hostname:port" */ if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { return 1; } if (u->field_set & (1 << UF_PORT)) { /* Don't bother with endp; we've already validated the string */ unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10); /* Ports have a max value of 2^16 */ if (v > 0xffff) { return 1; } u->port = (uint16_t) v; } return 0; } void http_parser_pause(http_parser *parser, int paused) { /* Users should only be pausing/unpausing a parser that is not in an error * state. In non-debug builds, there's not much that we can do about this * other than ignore it. */ if (HTTP_PARSER_ERRNO(parser) == HPE_OK || HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); } else { assert(0 && "Attempting to pause parser in error state"); } } int http_body_is_final(const struct http_parser *parser) { return parser->state == s_message_done; } unsigned long http_parser_version(void) { return HTTP_PARSER_VERSION_MAJOR * 0x10000 | HTTP_PARSER_VERSION_MINOR * 0x00100 | HTTP_PARSER_VERSION_PATCH * 0x00001; } sysdig-0.26.4/userspace/libsinsp/http_parser.h000066400000000000000000000331001352731327100214270ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef http_parser_h #define http_parser_h #ifdef __cplusplus extern "C" { #endif /* Also update SONAME in the Makefile whenever you change these. */ #define HTTP_PARSER_VERSION_MAJOR 2 #define HTTP_PARSER_VERSION_MINOR 7 #define HTTP_PARSER_VERSION_PATCH 1 #include #if defined(_WIN32) && !defined(__MINGW32__) && \ (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__) #include #include typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int16 int16_t; typedef unsigned __int16 uint16_t; typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #else #include #endif /* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run * faster */ #ifndef HTTP_PARSER_STRICT # define HTTP_PARSER_STRICT 1 #endif /* Maximum header size allowed. If the macro is not defined * before including this header then the default is used. To * change the maximum header size, define the macro in the build * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove * the effective limit on the size of the header, define the macro * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) */ #ifndef HTTP_MAX_HEADER_SIZE # define HTTP_MAX_HEADER_SIZE (80*1024) #endif typedef struct http_parser http_parser; typedef struct http_parser_settings http_parser_settings; /* Callbacks should return non-zero to indicate an error. The parser will * then halt execution. * * The one exception is on_headers_complete. In a HTTP_RESPONSE parser * returning '1' from on_headers_complete will tell the parser that it * should not expect a body. This is used when receiving a response to a * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: * chunked' headers that indicate the presence of a body. * * Returning `2` from on_headers_complete will tell parser that it should not * expect neither a body nor any future responses on this connection. This is * useful for handling responses to a CONNECT request which may not contain * `Upgrade` or `Connection: upgrade` headers. * * http_data_cb does not return data chunks. It will be called arbitrarily * many times for each string. E.G. you might get 10 callbacks for "on_url" * each providing just a few characters more data. */ typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); typedef int (*http_cb) (http_parser*); /* Request Methods */ #define HTTP_METHOD_MAP(XX) \ XX(0, DELETE, DELETE) \ XX(1, GET, GET) \ XX(2, HEAD, HEAD) \ XX(3, POST, POST) \ XX(4, PUT, PUT) \ /* pathological */ \ XX(5, CONNECT, CONNECT) \ XX(6, OPTIONS, OPTIONS) \ XX(7, TRACE, TRACE) \ /* WebDAV */ \ XX(8, COPY, COPY) \ XX(9, LOCK, LOCK) \ XX(10, MKCOL, MKCOL) \ XX(11, MOVE, MOVE) \ XX(12, PROPFIND, PROPFIND) \ XX(13, PROPPATCH, PROPPATCH) \ XX(14, SEARCH, SEARCH) \ XX(15, UNLOCK, UNLOCK) \ XX(16, BIND, BIND) \ XX(17, REBIND, REBIND) \ XX(18, UNBIND, UNBIND) \ XX(19, ACL, ACL) \ /* subversion */ \ XX(20, REPORT, REPORT) \ XX(21, MKACTIVITY, MKACTIVITY) \ XX(22, CHECKOUT, CHECKOUT) \ XX(23, MERGE, MERGE) \ /* upnp */ \ XX(24, MSEARCH, M-SEARCH) \ XX(25, NOTIFY, NOTIFY) \ XX(26, SUBSCRIBE, SUBSCRIBE) \ XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ /* RFC-5789 */ \ XX(28, PATCH, PATCH) \ XX(29, PURGE, PURGE) \ /* CalDAV */ \ XX(30, MKCALENDAR, MKCALENDAR) \ /* RFC-2068, section 19.6.1.2 */ \ XX(31, LINK, LINK) \ XX(32, UNLINK, UNLINK) \ enum http_method { #define XX(num, name, string) HTTP_##name = num, HTTP_METHOD_MAP(XX) #undef XX }; enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; /* Flag values for http_parser.flags field */ enum flags { F_CHUNKED = 1 << 0 , F_CONNECTION_KEEP_ALIVE = 1 << 1 , F_CONNECTION_CLOSE = 1 << 2 , F_CONNECTION_UPGRADE = 1 << 3 , F_TRAILING = 1 << 4 , F_UPGRADE = 1 << 5 , F_SKIPBODY = 1 << 6 , F_CONTENTLENGTH = 1 << 7 }; /* Map for errno-related constants * * The provided argument should be a macro that takes 2 arguments. */ #define HTTP_ERRNO_MAP(XX) \ /* No error */ \ XX(OK, "success") \ \ /* Callback-related errors */ \ XX(CB_message_begin, "the on_message_begin callback failed") \ XX(CB_url, "the on_url callback failed") \ XX(CB_header_field, "the on_header_field callback failed") \ XX(CB_header_value, "the on_header_value callback failed") \ XX(CB_headers_complete, "the on_headers_complete callback failed") \ XX(CB_body, "the on_body callback failed") \ XX(CB_message_complete, "the on_message_complete callback failed") \ XX(CB_status, "the on_status callback failed") \ XX(CB_chunk_header, "the on_chunk_header callback failed") \ XX(CB_chunk_complete, "the on_chunk_complete callback failed") \ \ /* Parsing-related errors */ \ XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ XX(HEADER_OVERFLOW, \ "too many header bytes seen; overflow detected") \ XX(CLOSED_CONNECTION, \ "data received after completed connection: close message") \ XX(INVALID_VERSION, "invalid HTTP version") \ XX(INVALID_STATUS, "invalid HTTP status code") \ XX(INVALID_METHOD, "invalid HTTP method") \ XX(INVALID_URL, "invalid URL") \ XX(INVALID_HOST, "invalid host") \ XX(INVALID_PORT, "invalid port") \ XX(INVALID_PATH, "invalid path") \ XX(INVALID_QUERY_STRING, "invalid query string") \ XX(INVALID_FRAGMENT, "invalid fragment") \ XX(LF_EXPECTED, "LF character expected") \ XX(INVALID_HEADER_TOKEN, "invalid character in header") \ XX(INVALID_CONTENT_LENGTH, \ "invalid character in content-length header") \ XX(UNEXPECTED_CONTENT_LENGTH, \ "unexpected content-length header") \ XX(INVALID_CHUNK_SIZE, \ "invalid character in chunk size header") \ XX(INVALID_CONSTANT, "invalid constant string") \ XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ XX(STRICT, "strict mode assertion failed") \ XX(PAUSED, "parser is paused") \ XX(UNKNOWN, "an unknown error occurred") /* Define HPE_* values for each errno value above */ #define HTTP_ERRNO_GEN(n, s) HPE_##n, enum http_errno { HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) }; #undef HTTP_ERRNO_GEN /* Get an http_errno value from an http_parser */ #define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) struct http_parser { /** PRIVATE **/ unsigned int type : 2; /* enum http_parser_type */ unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */ unsigned int state : 7; /* enum state from http_parser.c */ unsigned int header_state : 7; /* enum header_state from http_parser.c */ unsigned int index : 7; /* index into current matcher */ unsigned int lenient_http_headers : 1; uint32_t nread; /* # bytes read in various scenarios */ uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ /** READ-ONLY **/ unsigned short http_major; unsigned short http_minor; unsigned int status_code : 16; /* responses only */ unsigned int method : 8; /* requests only */ unsigned int http_errno : 7; /* 1 = Upgrade header was present and the parser has exited because of that. * 0 = No upgrade header present. * Should be checked when http_parser_execute() returns in addition to * error checking. */ unsigned int upgrade : 1; /** PUBLIC **/ void *data; /* A pointer to get hook to the "connection" or "socket" object */ }; struct http_parser_settings { http_cb on_message_begin; http_data_cb on_url; http_data_cb on_status; http_data_cb on_header_field; http_data_cb on_header_value; http_cb on_headers_complete; http_data_cb on_body; http_cb on_message_complete; /* When on_chunk_header is called, the current chunk length is stored * in parser->content_length. */ http_cb on_chunk_header; http_cb on_chunk_complete; }; enum http_parser_url_fields { UF_SCHEMA = 0 , UF_HOST = 1 , UF_PORT = 2 , UF_PATH = 3 , UF_QUERY = 4 , UF_FRAGMENT = 5 , UF_USERINFO = 6 , UF_MAX = 7 }; /* Result structure for http_parser_parse_url(). * * Callers should index into field_data[] with UF_* values iff field_set * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and * because we probably have padding left over), we convert any port to * a uint16_t. */ struct http_parser_url { uint16_t field_set; /* Bitmask of (1 << UF_*) values */ uint16_t port; /* Converted UF_PORT string */ struct { uint16_t off; /* Offset into buffer in which field starts */ uint16_t len; /* Length of run in buffer */ } field_data[UF_MAX]; }; /* Returns the library version. Bits 16-23 contain the major version number, * bits 8-15 the minor version number and bits 0-7 the patch level. * Usage example: * * unsigned long version = http_parser_version(); * unsigned major = (version >> 16) & 255; * unsigned minor = (version >> 8) & 255; * unsigned patch = version & 255; * printf("http_parser v%u.%u.%u\n", major, minor, patch); */ unsigned long http_parser_version(void); void http_parser_init(http_parser *parser, enum http_parser_type type); /* Initialize http_parser_settings members to 0 */ void http_parser_settings_init(http_parser_settings *settings); /* Executes the parser. Returns number of parsed bytes. Sets * `parser->http_errno` on error. */ size_t http_parser_execute(http_parser *parser, const http_parser_settings *settings, const char *data, size_t len); /* If http_should_keep_alive() in the on_headers_complete or * on_message_complete callback returns 0, then this should be * the last message on the connection. * If you are the server, respond with the "Connection: close" header. * If you are the client, close the connection. */ int http_should_keep_alive(const http_parser *parser); /* Returns a string version of the HTTP method. */ const char *http_method_str(enum http_method m); /* Return a string name of the given error */ const char *http_errno_name(enum http_errno err); /* Return a string description of the given error */ const char *http_errno_description(enum http_errno err); /* Initialize all http_parser_url members to 0 */ void http_parser_url_init(struct http_parser_url *u); /* Parse a URL; return nonzero on failure */ int http_parser_parse_url(const char *buf, size_t buflen, int is_connect, struct http_parser_url *u); /* Pause or un-pause the parser; a nonzero value pauses */ void http_parser_pause(http_parser *parser, int paused); /* Checks if this is the final chunk of the body. */ int http_body_is_final(const http_parser *parser); #ifdef __cplusplus } #endif #endif sysdig-0.26.4/userspace/libsinsp/http_reason.cpp000066400000000000000000000052641352731327100217670ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "http_reason.h" const std::map http_reason::m_http_reason = { { 100, "Continue" }, { 101, "Switching Protocols" }, { 102, "Processing" }, { 200, "OK" }, { 201, "Created" }, { 202, "Accepted" }, { 203, "Non-Authoritative Information" }, { 204, "No Content" }, { 205, "Reset Content" }, { 206, "Partial Content" }, { 207, "Multi Status" }, { 208, "Already Reported" }, { 226, "IM Used" }, { 300, "Multiple Choices" }, { 301, "Moved Permanently" }, { 302, "Found" }, { 303, "See Other" }, { 304, "Not Modified" }, { 305, "Use Proxy" }, { 307, "Temporary Redirect" }, { 308, "Permanent Redirect" }, { 400, "Bad Request" }, { 401, "Unauthorized" }, { 402, "Payment Required" }, { 403, "Forbidden" }, { 404, "Not Found" }, { 405, "Method Not Allowed" }, { 406, "Not Acceptable" }, { 407, "Proxy Authentication Required" }, { 408, "Request Time-out" }, { 409, "Conflict" }, { 410, "Gone" }, { 411, "Length Required" }, { 412, "Precondition Failed" }, { 413, "Request Entity Too Large" }, { 414, "Request-URI Too Long" }, { 415, "Unsupported Media Type" }, { 416, "Requested Range Not Satisfiable" }, { 417, "Expectation Failed" }, { 418, "I'm a Teapot" }, { 420, "Enhance Your Calm" }, { 421, "Misdirected Request" }, { 422, "Unprocessable Entity" }, { 423, "Locked" }, { 424, "Failed Dependency" }, { 426, "Upgrade Required" }, { 428, "Precondition Required" }, { 429, "Too Many Requests" }, { 431, "Request Header Fields Too Large" }, { 451, "Unavailable For Legal Reasons" }, { 500, "Internal Server Error" }, { 501, "Not Implemented" }, { 502, "Bad Gateway" }, { 503, "Service Unavailable" }, { 504, "Gateway Time-Out" }, { 505, "HTTP Version Not Supported" }, { 506, "Variant Also Negotiates" }, { 507, "Insufficient Storage" }, { 508, "Loop Detected" }, { 510, "Not Extended" }, { 511, "Network Authentication Required" } }; std::string http_reason::get(int status) { auto it = m_http_reason.find(status); if(it != m_http_reason.end()) { return it->second; } return ""; } sysdig-0.26.4/userspace/libsinsp/http_reason.h000066400000000000000000000014301352731327100214230ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #include class http_reason { public: static std::string get(int status); private: static const std::map m_http_reason; }; sysdig-0.26.4/userspace/libsinsp/ifinfo.cpp000066400000000000000000000230131352731327100207030ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "sinsp.h" #include "sinsp_int.h" sinsp_network_interfaces::sinsp_network_interfaces(sinsp* inspector) : m_inspector(inspector) { if(inet_pton(AF_INET6, "::1", m_ipv6_loopback_addr.m_b) != 1) { throw sinsp_exception("Could not convert ipv6 loopback address ::1 to ipv6addr struct"); } } sinsp_ipv4_ifinfo::sinsp_ipv4_ifinfo(uint32_t addr, uint32_t netmask, uint32_t bcast, const char* name) { m_addr = addr; m_netmask = netmask; m_bcast = bcast; m_name = name; } void sinsp_ipv4_ifinfo::convert_to_string(char * dest, const uint32_t addr) { sprintf( dest, "%d.%d.%d.%d", (addr & 0xFF), ((addr & 0xFF00) >> 8), ((addr & 0xFF0000) >> 16), ((addr & 0xFF000000) >> 24)); } string sinsp_ipv4_ifinfo::address() const { char str_addr[16]; convert_to_string(str_addr, m_addr); return string(str_addr); } string sinsp_ipv4_ifinfo::to_string() const { char s[100]; char str_addr[16]; char s_netmask[16]; char s_bcast[16]; convert_to_string(str_addr, m_addr); convert_to_string(s_netmask, m_netmask); convert_to_string(s_bcast, m_bcast); snprintf(s, sizeof(s), "%s inet %s netmask %s broadcast %s", m_name.c_str(), str_addr, s_netmask, s_bcast); return string(s); } uint32_t sinsp_network_interfaces::infer_ipv4_address(uint32_t destination_address) { vector::iterator it; // first try to find exact match for(it = m_ipv4_interfaces.begin(); it != m_ipv4_interfaces.end(); it++) { if(it->m_addr == destination_address) { return it->m_addr; } } // try to find an interface for the same subnet for(it = m_ipv4_interfaces.begin(); it != m_ipv4_interfaces.end(); it++) { if((it->m_addr & it->m_netmask) == (destination_address & it->m_netmask)) { return it->m_addr; } } // otherwise take the first non loopback interface for(it = m_ipv4_interfaces.begin(); it != m_ipv4_interfaces.end(); it++) { if(it->m_addr != LOOPBACK_ADDR) { return it->m_addr; } } return 0; } void sinsp_network_interfaces::update_fd(sinsp_fdinfo_t *fd) { ipv4tuple *pipv4info = &(fd->m_sockinfo.m_ipv4info); ipv6tuple *pipv6info = &(fd->m_sockinfo.m_ipv6info); // // only handle ipv4/ipv6 udp sockets // if(fd->m_type != SCAP_FD_IPV4_SOCK && fd->m_type != SCAP_FD_IPV6_SOCK) { return; } if(fd->m_type == SCAP_FD_IPV4_SOCK) { if(0 != pipv4info->m_fields.m_sip && 0 != pipv4info->m_fields.m_dip) { return; } if(0 == pipv4info->m_fields.m_sip) { uint32_t newaddr; newaddr = infer_ipv4_address(pipv4info->m_fields.m_dip); if(newaddr == pipv4info->m_fields.m_dip) { if(pipv4info->m_fields.m_sport == pipv4info->m_fields.m_dport) { return; } } pipv4info->m_fields.m_sip = newaddr; } else { uint32_t newaddr; newaddr = infer_ipv4_address(pipv4info->m_fields.m_sip); if(newaddr == pipv4info->m_fields.m_sip) { if(pipv4info->m_fields.m_sport == pipv4info->m_fields.m_dport) { return; } } pipv4info->m_fields.m_dip = newaddr; } } else if(fd->m_type == SCAP_FD_IPV6_SOCK) { if(ipv6addr::empty_address != pipv6info->m_fields.m_sip && ipv6addr::empty_address != pipv6info->m_fields.m_dip) { return; } if(ipv6addr::empty_address == pipv6info->m_fields.m_sip) { ipv6addr newaddr; newaddr = infer_ipv6_address(pipv6info->m_fields.m_dip); if(newaddr == pipv6info->m_fields.m_dip) { if(pipv6info->m_fields.m_sport == pipv6info->m_fields.m_dport) { return; } } pipv6info->m_fields.m_sip = newaddr; } else { ipv6addr newaddr; newaddr = infer_ipv6_address(pipv6info->m_fields.m_sip); if(newaddr == pipv6info->m_fields.m_sip) { if(pipv6info->m_fields.m_sport == pipv6info->m_fields.m_dport) { return; } } pipv6info->m_fields.m_dip = newaddr; } } } bool sinsp_network_interfaces::is_ipv4addr_in_subnet(uint32_t addr) { vector::iterator it; // // Accept everything that comes from 192.168.0.0/16 or 10.0.0.0/8 // if((addr & 0x000000ff) == 0x0000000a || (addr & 0x0000ffff) == 0x0000a8c0 || (addr & 0x00003fff) == 0x000010ac) { return true; } // try to find an interface for the same subnet for(it = m_ipv4_interfaces.begin(); it != m_ipv4_interfaces.end(); it++) { if((it->m_addr & it->m_netmask) == (addr & it->m_netmask)) { return true; } } return false; } bool sinsp_network_interfaces::is_ipv4addr_in_local_machine(uint32_t addr, sinsp_threadinfo* tinfo) { if(!tinfo->m_container_id.empty()) { const sinsp_container_info * container_info = m_inspector->m_container_manager.get_container(tinfo->m_container_id); // // Note: if we don't have container info, any pick we make is arbitrary. // To at least achieve consistency across client and server, we just match the host interface addresses. // if(container_info) { if(container_info->m_container_ip != 0) { // // We have a container info with a valid container IP. Let's use it. // if(addr == htonl(container_info->m_container_ip)) { return true; } } else { // // Container info is valid, but the IP address is zero. // Scan the list of the containers looking for matches. // If no match is found, we just jump to checking the // host interfaces. // if(!container_info->m_metadata_complete) { g_logger.format(sinsp_logger::SEV_DEBUG, "Checking IP address of container %s with incomplete metadata", tinfo->m_container_id.c_str()); } const unordered_map* clist = m_inspector->m_container_manager.get_containers(); for(auto it = clist->begin(); it != clist->end(); ++it) { if(!it->second.m_metadata_complete) { g_logger.format(sinsp_logger::SEV_DEBUG, "Checking IP address of container %s with incomplete metadata (in context of %s)", it->second.m_id.c_str(), tinfo->m_container_id.c_str()); } if(htonl(it->second.m_container_ip) == addr) { return true; } } } } } vector::iterator it; // try to find an interface that has the given IP as address for(it = m_ipv4_interfaces.begin(); it != m_ipv4_interfaces.end(); it++) { if(it->m_addr == addr) { return true; } } return false; } void sinsp_network_interfaces::import_ipv4_ifaddr_list(uint32_t count, scap_ifinfo_ipv4* plist) { if (count == 0) { return; } for(uint32_t j = 0; j < count; j++) { sinsp_ipv4_ifinfo info; info.m_addr = plist->addr; info.m_netmask = plist->netmask; info.m_bcast = plist->bcast; info.m_name = plist->ifname; m_ipv4_interfaces.push_back(info); plist++; } } ipv6addr sinsp_network_interfaces::infer_ipv6_address(ipv6addr &destination_address) { vector::iterator it; // first try to find exact match for(it = m_ipv6_interfaces.begin(); it != m_ipv6_interfaces.end(); it++) { if(destination_address == it->m_net) { return it->m_net; } } // try to find an interface for the same subnet for(it = m_ipv6_interfaces.begin(); it != m_ipv6_interfaces.end(); it++) { if(it->m_net.in_subnet(destination_address)) { return it->m_net; } } // otherwise take the first non loopback interface for(it = m_ipv6_interfaces.begin(); it != m_ipv6_interfaces.end(); it++) { if(it->m_net != m_ipv6_loopback_addr) { return it->m_net; } } return ipv6addr::empty_address; } bool sinsp_network_interfaces::is_ipv6addr_in_local_machine(ipv6addr &addr, sinsp_threadinfo* tinfo) { if(!tinfo->m_container_id.empty()) { // For now, not supporting ipv6 networking for containers. So always return false; return false; } vector::iterator it; // try to find an interface that has the given IP as address for(it = m_ipv6_interfaces.begin(); it != m_ipv6_interfaces.end(); it++) { if(addr.in_subnet(it->m_net)) { return true; } } return false; } void sinsp_network_interfaces::import_ipv6_ifaddr_list(uint32_t count, scap_ifinfo_ipv6* plist) { if (count == 0) { return; } for(uint32_t j = 0; j < count; j++) { sinsp_ipv6_ifinfo info; // Only saving the address portion. (Assumes // convention of first 48 bits for network, next 16 // bits for subnet). memcpy(info.m_net.m_b, plist->addr, SCAP_IPV6_ADDR_LEN); info.m_name = plist->ifname; m_ipv6_interfaces.push_back(info); plist++; } } void sinsp_network_interfaces::import_interfaces(scap_addrlist* paddrlist) { if(NULL != paddrlist) { clear(); import_ipv4_ifaddr_list(paddrlist->n_v4_addrs, paddrlist->v4list); import_ipv6_ifaddr_list(paddrlist->n_v6_addrs, paddrlist->v6list); } } void sinsp_network_interfaces::import_ipv4_interface(const sinsp_ipv4_ifinfo& ifinfo) { m_ipv4_interfaces.push_back(ifinfo); } void sinsp_network_interfaces::import_ipv6_interface(const sinsp_ipv6_ifinfo& ifinfo) { m_ipv6_interfaces.push_back(ifinfo); } vector* sinsp_network_interfaces::get_ipv4_list() { return &m_ipv4_interfaces; } vector* sinsp_network_interfaces::get_ipv6_list() { return &m_ipv6_interfaces; } sysdig-0.26.4/userspace/libsinsp/ifinfo.h000066400000000000000000000046301352731327100203540ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include "tuples.h" #define LOOPBACK_ADDR 0x0100007f #ifndef VISIBILITY_PRIVATE #define VISIBILITY_PRIVATE private: #endif // // network interface info ipv4 // class SINSP_PUBLIC sinsp_ipv4_ifinfo { public: sinsp_ipv4_ifinfo() {}; sinsp_ipv4_ifinfo(uint32_t addr, uint32_t netmask, uint32_t bcast, const char* name); string to_string() const; string address() const; uint32_t m_addr; uint32_t m_netmask; uint32_t m_bcast; string m_name; private: static void convert_to_string(char * dest, const uint32_t addr); }; // // network interface info ipv6 // class SINSP_PUBLIC sinsp_ipv6_ifinfo { public: sinsp_ipv6_ifinfo() {}; ipv6addr m_net; string m_name; }; class SINSP_PUBLIC sinsp_network_interfaces { public: sinsp_network_interfaces(sinsp* inspector); void import_interfaces(scap_addrlist* paddrlist); void import_ipv4_interface(const sinsp_ipv4_ifinfo& ifinfo); void update_fd(sinsp_fdinfo_t *fd); bool is_ipv4addr_in_subnet(uint32_t addr); bool is_ipv4addr_in_local_machine(uint32_t addr, sinsp_threadinfo* tinfo); void import_ipv6_interface(const sinsp_ipv6_ifinfo& ifinfo); bool is_ipv6addr_in_local_machine(ipv6addr &addr, sinsp_threadinfo* tinfo); vector* get_ipv4_list(); vector* get_ipv6_list(); inline void clear(); ipv6addr m_ipv6_loopback_addr; VISIBILITY_PRIVATE uint32_t infer_ipv4_address(uint32_t destination_address); void import_ipv4_ifaddr_list(uint32_t count, scap_ifinfo_ipv4* plist); ipv6addr infer_ipv6_address(ipv6addr &destination_address); void import_ipv6_ifaddr_list(uint32_t count, scap_ifinfo_ipv6* plist); vector m_ipv4_interfaces; vector m_ipv6_interfaces; sinsp* m_inspector; }; void sinsp_network_interfaces::clear() { m_ipv4_interfaces.clear(); m_ipv6_interfaces.clear(); }sysdig-0.26.4/userspace/libsinsp/ifinfo_test.cpp000066400000000000000000000075621352731327100217550ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #define VISIBILITY_PRIVATE #include "sinsp.h" #include "sinsp_int.h" #include "ifinfo.h" uint32_t parse_ipv4_addr(const char *dotted_notation) { uint32_t a, b, c, d; sscanf(dotted_notation, "%d.%d.%d.%d", &a, &b, &c, &d); return d << 24 | c << 16 | b << 8 | a; } uint32_t parse_ipv4_netmask(const char *dotted_notation) { return parse_ipv4_addr(dotted_notation); } uint32_t parse_ipv4_broadcast(const char *dotted_notation) { return parse_ipv4_addr(dotted_notation); } sinsp_ipv4_ifinfo make_ipv4_interface(const char *addr, const char *netmask, const char* broadcast, const char *name) { return sinsp_ipv4_ifinfo( parse_ipv4_addr(addr), parse_ipv4_netmask(netmask), parse_ipv4_broadcast(broadcast), name); } sinsp_ipv4_ifinfo make_ipv4_localhost() { return make_ipv4_interface("127.0.0.1", "255.0.0.0", "127.0.0.1", "lo"); } void convert_to_string(char* dest, uint32_t addr) { sprintf( dest, "%d.%d.%d.%d", (addr & 0xFF), ((addr & 0xFF00) >> 8), ((addr & 0xFF0000) >> 16), ((addr & 0xFF000000) >> 24)); } #define EXPECT_ADDR_EQ(dotted_notation,addr) {\ char buf[17];\ convert_to_string(buf,addr);\ EXPECT_STREQ(dotted_notation,buf);\ }; TEST(sinsp_network_interfaces, fd_is_of_wrong_type) { sinsp_fdinfo fd; fd.m_type = SCAP_FD_UNKNOWN; sinsp_network_interfaces interfaces; interfaces.update_fd(&fd); } TEST(sinsp_network_interfaces, socket_is_of_wrong_type) { sinsp_fdinfo fd; fd.m_type = SCAP_FD_IPV4_SOCK; fd.m_info.m_ipv4info.m_fields.m_l4proto = SCAP_L4_TCP; sinsp_network_interfaces interfaces; interfaces.update_fd(&fd); } TEST(sinsp_network_interfaces, sip_and_dip_are_not_zero) { sinsp_fdinfo fd; fd.m_type = SCAP_FD_IPV4_SOCK; fd.m_info.m_ipv4info.m_fields.m_l4proto = SCAP_L4_UDP; fd.m_info.m_ipv4info.m_fields.m_sip = 1; fd.m_info.m_ipv4info.m_fields.m_dip = 1; sinsp_network_interfaces interfaces; interfaces.update_fd(&fd); } TEST(sinsp_network_interfaces, infer_finds_exact_match) { sinsp_network_interfaces interfaces; interfaces.m_ipv4_interfaces.push_back(make_ipv4_localhost()); interfaces.m_ipv4_interfaces.push_back(make_ipv4_interface("192.168.22.149", "255.255.255.0", "192.168.22.255", "eth0")); EXPECT_ADDR_EQ("127.0.0.1",interfaces.infer_ipv4_address(parse_ipv4_addr("127.0.0.1"))); EXPECT_ADDR_EQ("192.168.22.149",interfaces.infer_ipv4_address(parse_ipv4_addr("192.168.22.149"))); } TEST(sinsp_network_interfaces, infer_finds_same_subnet) { sinsp_network_interfaces interfaces; interfaces.m_ipv4_interfaces.push_back(make_ipv4_localhost()); interfaces.m_ipv4_interfaces.push_back(make_ipv4_interface("192.168.22.149", "255.255.255.0", "192.168.22.255", "eth0")); EXPECT_ADDR_EQ("192.168.22.149",interfaces.infer_ipv4_address(parse_ipv4_addr("192.168.22.11"))); } TEST(sinsp_network_interfaces, infer_defaults_to_first_non_loopback) { sinsp_network_interfaces interfaces; interfaces.m_ipv4_interfaces.push_back(make_ipv4_localhost()); interfaces.m_ipv4_interfaces.push_back(make_ipv4_interface("192.168.22.149", "255.255.255.0", "192.168.22.255", "eth0")); interfaces.m_ipv4_interfaces.push_back(make_ipv4_interface("192.168.22.150", "255.255.255.0", "192.168.22.255", "eth1")); EXPECT_ADDR_EQ("192.168.22.149",interfaces.infer_ipv4_address(parse_ipv4_addr("193.168.22.11"))); }sysdig-0.26.4/userspace/libsinsp/internal_metrics.cpp000066400000000000000000000016561352731327100230040ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "sinsp.h" #include "sinsp_int.h" #ifdef GATHER_INTERNAL_STATS namespace internal_metrics { counter::~counter() { } counter::counter() { m_value = 0; } void registry::clear_all_metrics() { for(metric_map_iterator_t it = get_metrics().begin(); it != get_metrics().end(); it++) { it->second->clear(); } } } #endif sysdig-0.26.4/userspace/libsinsp/internal_metrics.h000066400000000000000000000051331352731327100224430ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #ifdef GATHER_INTERNAL_STATS #include #include #include #define INTERNAL_COUNTER(X) internal_metrics::counter *X namespace internal_metrics { class metric; class registry; class counter; class SINSP_PUBLIC metric_name { public: metric_name(std::string name, std::string description) { m_name = name; m_description = description; } bool operator<(const metric_name& other) const { return m_name> metric_map_t; typedef metric_map_t::iterator metric_map_iterator_t; counter& register_counter(const metric_name& name) { std::shared_ptr p; p = std::make_shared(); m_metrics[name] = p; return *p.get(); } metric_map_t& get_metrics() { return m_metrics; } void clear_all_metrics(); private: //template T& create_metric(const metric_name& name, Args... args) //{ // if (m_metrics.find(name) == std::end(m_metrics)) // { // m_metrics[name] = std::make_shared(args...); // } // return dynamic_cast(*m_metrics[name]); //} metric_map_t m_metrics; }; class SINSP_PUBLIC counter : public metric { public: ~counter(); counter(); void increment() { m_value++; } void decrement() { m_value--; } void clear() { m_value = 0; } const uint64_t get_value() { return m_value; } void process(processor& metric_processor) { metric_processor.process(*this); } private: uint64_t m_value; }; } #else #define INTERNAL_COUNTER(X) #endif // GATHER_INTERNAL_STATS sysdig-0.26.4/userspace/libsinsp/json_error_log.cpp000066400000000000000000000060551352731327100224630ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include "user_event_logger.h" #include "json_error_log.h" json_error_log g_json_error_log; json_error_log::json_error_log() : m_events_rate(.00333), // one event per 5 minutes m_events_max_burst(10) { } json_error_log::~json_error_log() { } void json_error_log::set_json_parse_errors_file(const std::string& filename) { m_json_parse_errors_file = filename; } // Note: not changing the rate/burst of any already-created bucket void json_error_log::set_events_rate(double events_rate, uint32_t max_burst) { m_events_rate = events_rate; m_events_max_burst = max_burst; } void json_error_log::log(const std::string &json, const std::string &errstr, uint64_t ts_ns, const std::string &uri) { time_t now = ts_ns / ONE_SECOND_IN_NS; if(m_json_parse_errors_file != "") { std::ofstream errs(m_json_parse_errors_file, std::ofstream::out | std::ofstream::app); char buf[sizeof("YYYY-MM-DDTHH:MM:SSZ")]; strftime(buf, sizeof(buf), "%FT%TZ", gmtime(&now)); errs << "*******************************" << std::endl; errs << "URI: " << uri << std::endl; errs << "Time (UTC): " << buf << std::endl; errs << "Error: " << errstr << std::endl; errs << "Json: " << json << std::endl; errs << "*******************************" << std::endl; errs.close(); } token_bucket &bucket = get_bucket(uri); if(bucket.claim(1, ts_ns)) { sinsp_user_event::tag_map_t tags; tags["source"] = "json_parser"; tags["uri"] = uri; tags["json_prefix"] = json.substr(0, 100); std::string event_name = "json_parse_error"; std::string desc = errstr; event_scope scope; if(m_machine_id.length()) { scope.add("host.mac", m_machine_id); } // Also emit a custom event noting the json parse failure. auto evt = sinsp_user_event(now, std::move(event_name), std::move(desc), std::move(scope.get_ref()), std::move(tags), user_event_logger::SEV_EVT_WARNING); g_logger.log("Logging user event: " + evt.to_string(), sinsp_logger::SEV_DEBUG); user_event_logger::log(evt, user_event_logger::SEV_EVT_WARNING); } } token_bucket &json_error_log::get_bucket(const std::string &uri) { auto it = m_buckets.lower_bound(uri); if(it == m_buckets.end() || it->first != uri) { it = m_buckets.emplace_hint(it, std::make_pair(uri, token_bucket())); it->second.init(m_events_rate, m_events_max_burst); } return it->second; } sysdig-0.26.4/userspace/libsinsp/json_error_log.h000066400000000000000000000034241352731327100221250ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #include #include "token_bucket.h" class json_error_log { public: json_error_log(); virtual ~json_error_log(); // Upon any json parsing error, write the error and json // document that had the error to this file. Not enabled by // default. void set_json_parse_errors_file(const std::string& filename); void set_events_rate(double events_rate, uint32_t max_burst); // Possibly log a json parsing error, depending on whether or // not the above filename was set. void log(const std::string &json, const std::string &errstr, uint64_t ts_ns, const std::string &uri); void set_machine_id(const std::string& machine_id); private: // Return a token bucket limiting errors related to the // configured uri, creating it if necessary. token_bucket &get_bucket(const std::string &uri); std::string m_json_parse_errors_file; std::string m_machine_id; double m_events_rate; uint32_t m_events_max_burst; // Rate-limit json parse error events by uri. std::map m_buckets; }; inline void json_error_log::set_machine_id(const std::string& machine_id) { m_machine_id = machine_id; } extern json_error_log g_json_error_log; sysdig-0.26.4/userspace/libsinsp/json_query.cpp000066400000000000000000000051531352731327100216340ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // json_parser.h // // jq wrapper // #ifdef __linux__ #include "json_query.h" #include "sinsp.h" json_query::json_query(const std::string& json, const std::string& filter, bool dbg) : m_jq(jq_init()), m_input{0}, m_result{0}, m_processed(false) { if(!m_jq) { cleanup(); } process(json, filter, dbg); } json_query::~json_query() { cleanup(); } bool json_query::process(const std::string& json, const std::string& filter, bool dbg) { cleanup(m_input); cleanup(m_result); clear(); if(!m_jq) { cleanup(); } if(!jq_compile(m_jq, filter.c_str())) { m_error = "Filter parsing failed."; return false; } m_input = jv_parse/*_sized*/(json.c_str()/*, json.length()*/); if (!jv_is_valid(m_input)) { cleanup(m_input, "JSON parse error."); return false; } jq_start(m_jq, m_input, dbg ? JQ_DEBUG_TRACE : 0); m_input = jv_null(); // jq_start() freed it m_result = jq_next(m_jq); if (!jv_is_valid(m_result)) { cleanup(m_result, "json_query filtering result invalid."); return false; } m_json = json; m_filter = filter; return m_processed = true; } const std::string& json_query::result(int flags) { if(m_processed) { static const std::string ret; if(!m_error.empty()) { return ret; } char* buf; size_t len; FILE* f = open_memstream(&buf, &len); if(f == NULL) { m_error = "Can't open memory stream for writing."; return ret; } jv_dumpf(m_result, f, flags); m_result = jv_null(); clear(); fclose (f); m_filtered_json.assign(buf, len); free (buf); m_processed = false; } return m_filtered_json; } void json_query::clear() { m_result = jv_null(); m_input = jv_null(); m_filtered_json.clear(); m_error.clear(); m_processed = false; } void json_query::cleanup() { if(m_jq) { cleanup(m_input); cleanup(m_result); clear(); jq_teardown(&m_jq); m_jq = 0; } else { throw std::runtime_error("json_query handle is null."); } } void json_query::cleanup(jv& j, const std::string& msg) { if(j.u.ptr) { jv_free(j); j = jv_null(); } m_error = msg; } #endif // __linux__ sysdig-0.26.4/userspace/libsinsp/json_query.h000066400000000000000000000044541352731327100213040ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // json_parser.h // // jq wrapper // #ifdef __linux__ #pragma once // // Avoid some annoying "function not used" from jq headers; // this pragma affects this header file only and has no effect // on source file including it; // see https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html // #ifdef __GNUC__ # pragma GCC system_header #endif #include #include #include // jq is not C++-friendly extern "C" { #include "jv.h" #include "jq.h" } #include class json_query { public: json_query(const std::string& json = "", const std::string& filter = "", bool dbg = false); ~json_query(); void set_json(const std::string& json); const std::string& get_json() const; void set_filter(const std::string& filter); const std::string& get_filter() const; bool process(const std::string& json, const std::string& filter, bool dbg = false); const std::string& result(int flags = 0); const std::string& get_error() const; private: void clear(); void cleanup(); void cleanup(jv& j, const std::string& msg = ""); jq_state* m_jq; std::string m_json; std::string m_filter; std::string m_filtered_json; jv m_input; jv m_result; bool m_processed; mutable std::string m_error; }; inline void json_query::set_json(const std::string& json) { m_json = json; } inline const std::string& json_query::get_json() const { return m_json; } inline void json_query::set_filter(const std::string& filter) { m_filter = filter; } inline const std::string& json_query::get_filter() const { return m_filter; } inline const std::string& json_query::get_error() const { return m_error; } #endif // __linux__ sysdig-0.26.4/userspace/libsinsp/k8s.cpp000066400000000000000000000206631352731327100201460ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s.cpp // #ifndef CYGWING_AGENT #include "k8s.h" #include "k8s_component.h" #include "k8s_dispatcher.h" #include "sinsp.h" #include "sinsp_int.h" #include #include #include #include #include k8s_component::type_map k8s::m_components; k8s::k8s(const std::string& uri, bool is_captured, #ifdef HAS_CAPTURE ssl_ptr_t ssl, bt_ptr_t bt, bool block, #endif // HAS_CAPTURE filter_ptr_t event_filter, ext_list_ptr_t extensions, bool events_only) : m_state(is_captured), m_event_filter(event_filter) #ifdef HAS_CAPTURE ,m_net(uri.empty() ? nullptr : new k8s_net(*this, m_state, uri, ssl, bt, event_filter, block)) #endif { g_logger.log(std::string("Creating K8s object for [" + (uri.empty() ? std::string("capture replay") : uri) + ']'), sinsp_logger::SEV_DEBUG); if(m_components.empty()) { if(events_only) { m_components.insert({ k8s_component::K8S_EVENTS, "events"}); return; } m_components.insert({ k8s_component::K8S_NODES, "nodes" }); m_components.insert({ k8s_component::K8S_NAMESPACES, "namespaces" }); m_components.insert({ k8s_component::K8S_PODS, "pods" }); m_components.insert({ k8s_component::K8S_REPLICATIONCONTROLLERS, "replicationcontrollers" }); m_components.insert({ k8s_component::K8S_SERVICES, "services" }); if(event_filter) { m_components.insert({ k8s_component::K8S_EVENTS, "events"}); } if(extensions) { for(const auto& ext : *extensions) { if(ext == "daemonsets") { m_components.insert({ k8s_component::K8S_DAEMONSETS, "daemonsets" }); } else if(ext == "deployments") { m_components.insert({ k8s_component::K8S_DEPLOYMENTS, "deployments" }); } else if(ext == "replicasets") { m_components.insert({ k8s_component::K8S_REPLICASETS, "replicasets" }); } } } } } k8s::~k8s() { stop_watch(); cleanup(); } void k8s::stop_watch() { #ifdef HAS_CAPTURE if(m_net) { m_net->stop_watching(); } #endif } void k8s::cleanup() { #ifdef HAS_CAPTURE delete m_net; m_net = nullptr; #endif } void k8s::check_components() { #ifdef HAS_CAPTURE if(m_net) { for (auto it = m_components.cbegin(); it != m_components.cend();) { if(m_net->has_handler(*it)) { k8s_net::handler_ptr_t handler = k8s_net::get_handler(m_net->handlers(), *it); if(handler) { k8s_handler::api_error_ptr handler_error = handler->error(); // HTTP error > 400 means non-existing, forbidden, etc. if(handler_error && handler_error->code() >= 400) { std::string handler_name = handler->name(); if(!k8s_component::is_critical(handler_name)) { g_logger.log("K8s: removing " + handler_name + " due to HTTP error " + std::to_string(handler_error->code()) + ", reason: " + handler_error->reason() + ", message: " + handler_error->message(), sinsp_logger::SEV_WARNING); m_components.erase(it++); continue; } else { throw sinsp_exception(handler_error->to_string()); } } } } else { if(it->first != k8s_component::K8S_EVENTS) { m_net->add_handler(*it); } else if(m_event_filter) // events only if filter is enabled { m_net->add_handler(*it); } } ++it; } } else { throw sinsp_exception("K8s net object is null."); } #endif } const k8s_state_t& k8s::get_state() { return m_state; } void k8s::watch() { #ifdef HAS_CAPTURE if(m_net) { check_components(); m_net->watch(); } #endif } void k8s::simulate_watch_event(const std::string& json, int version) { Json::Value root; Json::Reader reader; k8s_component::type component_type = k8s_component::K8S_COMPONENT_COUNT; if(reader.parse(json, root, false)) { const Json::Value& kind = root["kind"]; if(!kind.isNull() && kind.isString()) { std::string type = kind.asString(); if(type == "Namespace") { component_type = k8s_component::K8S_NAMESPACES; } else if(type == "Node") { component_type = k8s_component::K8S_NODES; } else if(type == "Pod") { component_type = k8s_component::K8S_PODS; } else if(type == "ReplicationController") { component_type = k8s_component::K8S_REPLICATIONCONTROLLERS; } else if(type == "ReplicaSet") { component_type = k8s_component::K8S_REPLICASETS; } else if(type == "Service") { component_type = k8s_component::K8S_SERVICES; } else if(type == "DaemonSet") { component_type = k8s_component::K8S_DAEMONSETS; } else if(type == "Deployment") { component_type = k8s_component::K8S_DEPLOYMENTS; } else if(type == "EventList") { component_type = k8s_component::K8S_EVENTS; } else { g_logger.log("Unrecognized component type: " + type, sinsp_logger::SEV_ERROR); return; } } else { g_logger.log("Component type not found in JSON", sinsp_logger::SEV_ERROR); return; } } else { g_logger.log("Error parsing JSON", sinsp_logger::SEV_ERROR); return; } if(m_state.get_capture_version() == k8s_state_t::CAPTURE_VERSION_NONE) { m_state.set_capture_version(version); } static bool version_logged = false; if(!version_logged) { g_logger.log("K8s capture version: " + std::to_string(version), sinsp_logger::SEV_DEBUG); version_logged = true; } switch(version) { case k8s_state_t::CAPTURE_VERSION_1: // old capture format if(component_type < k8s_component::K8S_COMPONENT_COUNT) { if(m_dispatch_map.find(component_type) == m_dispatch_map.end()) { m_dispatch_map[component_type] = std::unique_ptr(new k8s_dispatcher(component_type, m_state)); } m_dispatch_map[component_type]->extract_data(root, false); } else { throw sinsp_exception(std::string("K8s capture: unknown component type (") + std::to_string(component_type) + ")"); } break; case k8s_state_t::CAPTURE_VERSION_2: if(component_type < k8s_component::K8S_COMPONENT_COUNT) { if(m_handler_map.find(component_type) == m_handler_map.end()) { m_handler_map[component_type] = k8s_net::make_handler(m_state, component_type, false); } if(m_handler_map[component_type]) { m_handler_map[component_type]->handle_json(std::move(root)); } else { throw sinsp_exception(std::string("K8s capture replay: error creating ") + k8s_component::get_name(component_type) + " handler"); } } else { throw sinsp_exception(std::string("K8s capture: unknown component type (") + std::to_string(component_type) + ")"); } break; default: throw sinsp_exception(std::string("K8s capture: invalid capture version (") + std::to_string(version) + ")"); } } std::size_t k8s::count(k8s_component::type component) const { switch (component) { case k8s_component::K8S_NODES: return m_state.get_nodes().size(); case k8s_component::K8S_NAMESPACES: return m_state.get_namespaces().size(); case k8s_component::K8S_PODS: return m_state.get_pods().size(); case k8s_component::K8S_REPLICATIONCONTROLLERS: return m_state.get_rcs().size(); case k8s_component::K8S_REPLICASETS: return m_state.get_rss().size(); case k8s_component::K8S_SERVICES: return m_state.get_services().size(); case k8s_component::K8S_DAEMONSETS: return m_state.get_daemonsets().size(); case k8s_component::K8S_DEPLOYMENTS: return m_state.get_deployments().size(); case k8s_component::K8S_EVENTS: return m_state.get_events().size(); case k8s_component::K8S_COMPONENT_COUNT: default: break; } std::ostringstream os; os << "Unknown component " << static_cast(component); throw sinsp_exception(os.str()); } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/k8s.h000066400000000000000000000070011352731327100176020ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s.h // // extracts needed data from the k8s REST API interface // #pragma once #include "json/json.h" #include "k8s_component.h" #include "k8s_state.h" #include "k8s_event_data.h" #include "k8s_net.h" #include "sinsp_auth.h" #include #include class k8s_dispatcher; class k8s { public: #ifdef HAS_CAPTURE typedef sinsp_ssl::ptr_t ssl_ptr_t; typedef sinsp_bearer_token::ptr_t bt_ptr_t; #endif // HAS_CAPTURE typedef k8s_component::ext_list_ptr_t ext_list_ptr_t; typedef user_event_filter_t::ptr_t filter_ptr_t; k8s(const std::string& uri = "http://localhost:80", bool is_captured = false, #ifdef HAS_CAPTURE ssl_ptr_t ssl = 0, bt_ptr_t bt = 0, bool block = false, #endif // HAS_CAPTURE filter_ptr_t event_filter = nullptr, ext_list_ptr_t extensions = nullptr, bool events_only = false); ~k8s(); std::size_t count(k8s_component::type component) const; void check_components(); const k8s_state_t& get_state(); void clear_events(); void set_machine_id(const std::string& machine_id); std::string get_machine_id() const; void watch(); void stop_watching(); bool is_alive() const; #ifdef HAS_CAPTURE typedef k8s_state_t::event_list_t event_list_t; const event_list_t& get_capture_events() const { return m_state.get_capture_events(); } std::string dequeue_capture_event() { return m_state.dequeue_capture_event(); } #endif // HAS_CAPTURE // version: // - 1 to support k8s events captured in old format (before refactoring) // - 2 to support k8s events captured in new format (after refactoring) void simulate_watch_event(const std::string& json, int version = 2); private: void stop_watch(); void cleanup(); k8s_state_t m_state; filter_ptr_t m_event_filter; typedef std::map> dispatch_map_t; typedef std::map> handler_map_t; // dispatch map is deprecated and serves only for backward compatibility with captures with old sysdig dispatch_map_t m_dispatch_map; handler_map_t m_handler_map; #ifdef HAS_CAPTURE k8s_net* m_net = nullptr; #endif // a utility member containing pairs of enumerated values and component names static k8s_component::type_map m_components; friend class k8s_test; }; inline bool k8s::is_alive() const { #ifdef HAS_CAPTURE ASSERT(m_net); return m_net->is_healthy(); #endif return true; } inline void k8s::clear_events() { m_state.clear_events(); } inline std::string k8s::get_machine_id() const { #ifdef HAS_CAPTURE if(m_net) { return m_net->get_machine_id(); } #endif // HAS_CAPTURE return ""; } inline void k8s::set_machine_id(const std::string& machine_id) { #ifdef HAS_CAPTURE if(m_net) { m_net->set_machine_id(machine_id); } else { g_logger.log("K8s machine ID (MAC) setting attempted on null net object; " "scope may not be available for events.", sinsp_logger::SEV_WARNING); } #endif // HAS_CAPTURE } sysdig-0.26.4/userspace/libsinsp/k8s_api_error.cpp000066400000000000000000000027041352731327100222040ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_api_error.cpp // #include "k8s_api_error.h" k8s_api_error::k8s_api_error(const msg_data& data, const Json::Value& err): m_data(data), m_status(get_string(err, "status")), m_message(get_string(err, "message")), m_reason(get_string(err, "reason")), m_details(get_string(err, "details")), m_code(get_int(err, "code")) { } k8s_api_error::~k8s_api_error() { } std::string k8s_api_error::get_string(const Json::Value& obj, const std::string& name) { std::string value; const Json::Value& val = obj[name]; if(!val.isNull() && val.isConvertibleTo(Json::stringValue)) { value = val.asString(); } return value; } int k8s_api_error::get_int(const Json::Value& obj, const std::string& name) { int value = 0; const Json::Value& val = obj[name]; if(!val.isNull() && val.isConvertibleTo(Json::intValue)) { value = val.asInt(); } return value; } sysdig-0.26.4/userspace/libsinsp/k8s_api_error.h000066400000000000000000000053041352731327100216500ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_api_error.h // #pragma once #include "json/json.h" #include "k8s_component.h" class k8s_api_error { public: typedef k8s_component::msg_reason msg_reason; typedef k8s_component::msg_data msg_data; k8s_api_error(const msg_data& data, const Json::Value& err); ~k8s_api_error(); const std::string& component_name() const; const std::string& component_id() const; const std::string& component_namespace() const; const std::string& component_kind() const; const std::string& metadata() const; const std::string& status() const; const std::string& message() const; const std::string& reason() const; const std::string& details() const; int code() const; std::string to_string() const; private: static std::string get_string(const Json::Value& obj, const std::string& name); static int get_int(const Json::Value& obj, const std::string& name); msg_data m_data; std::string m_meta; std::string m_status; std::string m_message; std::string m_reason; std::string m_details; int m_code; }; inline const std::string& k8s_api_error::component_name() const { return m_data.m_name; } inline const std::string& k8s_api_error::component_id() const { return m_data.m_uid; } inline const std::string& k8s_api_error::component_namespace() const { return m_data.m_namespace; } inline const std::string& k8s_api_error::component_kind() const { return m_data.m_kind; } inline const std::string& k8s_api_error::metadata() const { return m_meta; } inline const std::string& k8s_api_error::status() const { return m_status; } inline const std::string& k8s_api_error::message() const { return m_message; } inline const std::string& k8s_api_error::reason() const { return m_reason; } inline const std::string& k8s_api_error::details() const { return m_details; } inline int k8s_api_error::code() const { return m_code; } inline std::string k8s_api_error::to_string() const { std::ostringstream os; os << "K8s API error; Status: " << m_status << ", " "Message: " << m_message << ", " "Reason: " << m_reason << ", " "Details: " << m_details << ", " "Code: " << m_message; return os.str(); } sysdig-0.26.4/userspace/libsinsp/k8s_api_handler.cpp000066400000000000000000000055601352731327100224730ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_api_handler.cpp // #ifndef CYGWING_AGENT #ifdef HAS_CAPTURE #include "k8s_api_handler.h" #include "sinsp.h" #include "sinsp_int.h" // filters normalize state and event JSONs, so they can be processed generically: // event is turned into a single-entry array, state is turned into an array of ADDED events k8s_api_handler::k8s_api_handler(collector_ptr_t collector, const std::string& url, const std::string& path, const std::string& filter, const std::string& http_version #ifdef HAS_CAPTURE ,ssl_ptr_t ssl ,bt_ptr_t bt ,bool blocking_socket #endif // HAS_CAPTURE ): k8s_handler("k8s_api_handler", false, #ifdef HAS_CAPTURE url, path, filter, ".", "", collector, http_version, 1000L, ssl, bt, false, true, std::make_shared(), blocking_socket, #endif // HAS_CAPTURE ~0, nullptr) { } k8s_api_handler::~k8s_api_handler() { } bool k8s_api_handler::handle_component(const Json::Value& json, const msg_data* data) { m_error = false; if(!json.isNull()) { if(json.isArray()) { for(const auto& version : json) { if(version.isConvertibleTo(Json::stringValue)) { m_extensions.push_back(version.asString()); } else { g_logger.log("K8s API handler error: could not extract API versions or extensions from JSON.", sinsp_logger::SEV_ERROR); m_error = true; return false; } } } else if(json.isConvertibleTo(Json::stringValue)) { m_extensions.push_back(json.asString()); } else { g_logger.log("K8s API handler error: could not extract API versions or extensions from JSON.", sinsp_logger::SEV_ERROR); m_error = true; return false; } m_data_received = true; } else { g_logger.log("K8s API handler error: json is null.", sinsp_logger::SEV_ERROR); m_error = true; return false; } return true; } void k8s_api_handler::handle_json(Json::Value&& root) { if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log("K8S API handler [" + json_as_string(root) + "] reply:\n", sinsp_logger::SEV_TRACE); } handle_component(root); } bool k8s_api_handler::has(const std::string& version) const { for(const auto& ver : m_extensions) { if(ver == version) { return true; } } return false; } #endif // HAS_CAPTURE #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/k8s_api_handler.h000066400000000000000000000030371352731327100221350ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_api_handler.h // #ifdef HAS_CAPTURE #pragma once #include "json/json.h" #include "k8s_handler.h" class k8s_api_handler : public k8s_handler { public: typedef std::vector api_list_t; k8s_api_handler(collector_ptr_t collector, const std::string& url, const std::string& path, const std::string& filter, const std::string& http_version = "1.1", ssl_ptr_t ssl = 0, bt_ptr_t bt = 0, bool blocking_socket = false); ~k8s_api_handler(); bool error() const; const api_list_t& extensions() const; bool has(const std::string& version) const; private: void handle_json(Json::Value&& root); bool handle_component(const Json::Value& json, const msg_data* data = 0); api_list_t m_extensions; bool m_error = false; }; inline bool k8s_api_handler::error() const { return m_error; } inline const k8s_api_handler::api_list_t& k8s_api_handler::extensions() const { return m_extensions; } #endif // HAS_CAPTURE sysdig-0.26.4/userspace/libsinsp/k8s_component.cpp000066400000000000000000000620041352731327100222230ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_component.cpp // #include "k8s_component.h" #include "k8s_state.h" #include "sinsp.h" #include "sinsp_int.h" #include "user_event.h" #include #include // // container // k8s_container::k8s_container() { } k8s_container::k8s_container(const std::string& name, const port_list& ports): m_name(name), m_ports(ports) { } k8s_container::k8s_container(const k8s_container& other): m_name(other.m_name), m_ports(other.m_ports) { } k8s_container::k8s_container(k8s_container&& other): m_name(std::move(other.m_name)), m_ports(std::move(other.m_ports)) { } k8s_container& k8s_container::operator=(const k8s_container& other) { m_name = other.m_name; m_ports = other.m_ports; return *this; } bool k8s_container::has_port(const std::string& port_name) const { for(const auto& port : m_ports) { if (port.get_name() == port_name) { return true; } } return false; } const k8s_container::port* k8s_container::get_port(const std::string& port_name) const { for(const auto& port : m_ports) { if (port.get_name() == port_name) { return &port; } } return 0; } // // component // const k8s_component::type_map k8s_component::list = { { k8s_component::K8S_NODES, "nodes" }, { k8s_component::K8S_NAMESPACES, "namespaces" }, { k8s_component::K8S_PODS, "pods" }, { k8s_component::K8S_REPLICATIONCONTROLLERS, "replicationcontrollers" }, { k8s_component::K8S_REPLICASETS, "replicasets" }, { k8s_component::K8S_SERVICES, "services" }, { k8s_component::K8S_DAEMONSETS, "daemonsets" }, { k8s_component::K8S_DEPLOYMENTS, "deployments" }, { k8s_component::K8S_EVENTS, "events" } }; k8s_component::k8s_component(type comp_type, const std::string& name, const std::string& uid, const std::string& ns) : m_type(comp_type), m_name(name), m_uid(uid), m_ns(ns) { } k8s_component::~k8s_component() { } k8s_pair_list k8s_component::extract_object(const Json::Value& object, const std::string& name) { k8s_pair_list entry_list; if(!object.isNull()) { const Json::Value& entries = object[name]; if(!entries.isNull()) { Json::Value::Members members = entries.getMemberNames(); for (auto& member : members) { const Json::Value& val = entries[member]; if(!val.isNull() && val.isString()) { entry_list.emplace_back(k8s_pair_t(member, val.asString())); } } } } return entry_list; } std::string k8s_component::get_name_u(type t) { switch (t) { case K8S_NAMESPACES: return "NAMESPACE"; case K8S_NODES: return "NODE"; case K8S_PODS: return "POD"; case K8S_REPLICATIONCONTROLLERS: return "REPLICATIONCONTROLLER"; case K8S_REPLICASETS: return "REPLICASET"; case K8S_SERVICES: return "SERVICE"; case K8S_DAEMONSETS: return "DAEMONSET"; case K8S_DEPLOYMENTS: return "DEPLOYMENT"; case K8S_EVENTS: return "EVENT"; case K8S_COMPONENT_COUNT: default: break; } std::ostringstream os; os << "Unknown component type " << static_cast(t); throw sinsp_exception(os.str().c_str()); } std::string k8s_component::get_name(type t) { switch (t) { case K8S_NAMESPACES: return "namespaces"; case K8S_NODES: return "nodes"; case K8S_PODS: return "pods"; case K8S_REPLICATIONCONTROLLERS: return "replicationcontrollers"; case K8S_REPLICASETS: return "replicasets"; case K8S_SERVICES: return "services"; case K8S_DAEMONSETS: return "daemonsets"; case K8S_DEPLOYMENTS: return "deployments"; case K8S_EVENTS: return "events"; case K8S_COMPONENT_COUNT: default: break; } std::ostringstream os; os << "Unknown component type " << static_cast(t); throw sinsp_exception(os.str().c_str()); } k8s_component::type k8s_component::get_type(const std::string& name) { if(name == "namespaces") { return K8S_NAMESPACES; } else if(name == "nodes") { return K8S_NODES; } else if(name == "pods") { return K8S_PODS; } else if(name == "replicationcontrollers") { return K8S_REPLICATIONCONTROLLERS; } else if(name == "replicasets") { return K8S_REPLICASETS; } else if(name == "services") { return K8S_SERVICES; } else if(name == "daemonsets") { return K8S_DAEMONSETS; } else if(name == "deployments") { return K8S_DEPLOYMENTS; } else if(name == "events") { return K8S_EVENTS; } std::ostringstream os; os << "K8s: Unknown component name " << name; throw sinsp_exception(os.str().c_str()); } std::string k8s_component::get_selector(type t) { switch (t) { case K8S_PODS: return "?fieldSelector=status.phase%3DRunning"; default: break; } return ""; } std::string k8s_component::get_selector(const component_pair& p) { return get_selector(p.first); } std::string k8s_component::get_selector(const std::string& name) { return get_selector(get_type(name)); } bool k8s_component::is_critical(type t) { switch (t) { case K8S_NODES: case K8S_NAMESPACES: case K8S_PODS: case K8S_REPLICATIONCONTROLLERS: case K8S_SERVICES: return true; case K8S_EVENTS: case K8S_REPLICASETS: case K8S_DAEMONSETS: case K8S_DEPLOYMENTS: default: break; } return false; } bool k8s_component::is_critical(const component_pair& p) { return is_critical(p.first); } bool k8s_component::is_critical(const std::string& name) { return is_critical(get_type(name)); } std::string k8s_component::get_api(type t, ext_list_ptr_t extensions) { switch (t) { case K8S_NAMESPACES: case K8S_NODES: case K8S_PODS: case K8S_REPLICATIONCONTROLLERS: case K8S_SERVICES: case K8S_EVENTS: return "/api/v1/"; case K8S_REPLICASETS: case K8S_DAEMONSETS: case K8S_DEPLOYMENTS: if(extensions && extensions->size()) { return "/apis/extensions/v1beta1/"; } else { return ""; } case K8S_COMPONENT_COUNT: default: break; } std::ostringstream os; os << "K8s: Unknown component type " << static_cast(t); throw sinsp_exception(os.str().c_str()); } std::string k8s_component::get_api(const component_pair& p, ext_list_ptr_t extensions) { return get_api(p.first, extensions); } std::string k8s_component::get_api(const std::string& name, ext_list_ptr_t extensions) { return get_api(get_type(name), extensions); } k8s_pair_t* k8s_component::get_label(const k8s_pair_t& label) { for (auto& lbl : m_labels) { if((lbl.first == label.first) && (lbl.second == label.second)) { return &lbl; } } return 0; } void k8s_component::add_labels(k8s_pair_list&& labels) { for (auto& label : labels) { if(!get_label(label)) { emplace_label(std::move(label)); } } } k8s_pair_t* k8s_component::get_selector(const k8s_pair_t& selector) { for (auto& sel : m_selectors) { if((sel.first == selector.first) && (sel.second == selector.second)) { return &sel; } } return 0; } void k8s_component::add_selectors(k8s_pair_list&& selectors) { for (auto& selector : selectors) { if(!get_selector(selector)) { emplace_selector(std::move(selector)); } } } // TODO: proper selection process is more complicated, see “Labels and Selectors” at // http://kubernetes.io/v1.0/docs/user-guide/labels.html bool k8s_component::selector_in_labels(const k8s_pair_t& selector, const k8s_pair_list& labels) const { if(!labels.size()) { return false; } for(const auto& label : labels) { if(label.first == selector.first && label.second == selector.second) { return true; } } return false; } bool k8s_component::selectors_in_labels(const k8s_pair_list& labels) const { const k8s_pair_list& selectors = get_selectors(); if(!labels.size() || !selectors.size()) { return false; } for(const auto& selector : selectors) { if(!selector_in_labels(selector, labels)) { return false; } } return true; } // // namespace // k8s_ns_t::k8s_ns_t(const std::string& name, const std::string& uid, const std::string& ns) : k8s_component(COMPONENT_TYPE, name, uid, ns) { } // // node // k8s_node_t::k8s_node_t(const std::string& name, const std::string& uid, const std::string& ns) : k8s_component(COMPONENT_TYPE, name, uid, ns) { } k8s_node_t::host_ip_list k8s_node_t::extract_addresses(const Json::Value& status) { host_ip_list address_list; if(!status.isNull()) { const Json::Value& addresses = status["addresses"]; if(!addresses.isNull() && addresses.isArray()) { for (auto& address : addresses) { if(address.isObject()) { Json::Value::Members addr_names_list = address.getMemberNames(); for (auto& entry : addr_names_list) { if(entry == "address") { const Json::Value& ip = address[entry]; if(!ip.isNull()) { address_list.emplace(ip.asString()); } } } } } } } return address_list; } // // pod // k8s_pod_t::k8s_pod_t(const std::string& name, const std::string& uid, const std::string& ns) : k8s_component(COMPONENT_TYPE, name, uid, ns) { } // // replicas // k8s_replicas_t::k8s_replicas_t(int spec_replicas, int stat_replicas): m_spec_replicas(spec_replicas), m_stat_replicas(stat_replicas) { } int k8s_replicas_t::get_count(const Json::Value& item, const std::string& replica_name) { if(!item.isNull()) { const Json::Value& replicas = item[replica_name]; if(!replicas.isNull() && replicas.isConvertibleTo(Json::intValue)) { return replicas.asInt(); } } if(g_logger.get_severity() >= sinsp_logger::SEV_DEBUG) { g_logger.log("K8s: Can not find " + replica_name + " in \n" + Json::FastWriter().write(item), sinsp_logger::SEV_DEBUG); std::string name; const Json::Value& tpl = item["template"]; if(!tpl.isNull()) { const Json::Value& md = tpl["metadata"]; if(!md.isNull()) { const Json::Value& lbl = md["labels"]; if(!lbl.isNull()) { const Json::Value& n = lbl["name"]; if(!n.isNull() && n.isString()) { name = n.asString(); } else { const Json::Value& n = lbl["app"]; if(!n.isNull() && n.isString()) { name = n.asString(); } } } } } g_logger.log("K8s: Can not determine number of replicas" + (name.empty() ? std::string() : std::string(" for ").append(name)), sinsp_logger::SEV_DEBUG); } return k8s_replicas_t::UNKNOWN_REPLICAS; } void k8s_replicas_t::set_replicas(k8s_replicas_t& replicas, const Json::Value& item) { int replica_count = k8s_replicas_t::get_count(item["spec"], "replicas"); if(replica_count != k8s_replicas_t::UNKNOWN_REPLICAS) { replicas.set_spec_replicas(replica_count); } replica_count = k8s_replicas_t::get_count(item["status"], "replicas"); if(replica_count != k8s_replicas_t::UNKNOWN_REPLICAS) { replicas.set_stat_replicas(replica_count); } else { int unavailable_replicas = k8s_replicas_t::get_count(item["status"], "unavailableReplicas"); int spec_replicas = replicas.get_spec_replicas(); if(spec_replicas != k8s_replicas_t::UNKNOWN_REPLICAS && unavailable_replicas < spec_replicas) { replicas.set_stat_replicas(spec_replicas - unavailable_replicas); } } } // // replication controller // k8s_rc_t::k8s_rc_t(const std::string& name, const std::string& uid, const std::string& ns, k8s_component::type type) : k8s_component(type, name, uid, ns) { } std::vector k8s_rc_t::get_selected_pods(const std::vector& pods) const { std::vector pod_vec; for(const auto& pod : pods) { if(selectors_in_labels(pod.get_labels()) && get_namespace() == pod.get_namespace()) { pod_vec.push_back(&pod); } } return pod_vec; } void k8s_rc_t::set_replicas(int spec, int stat) { set_spec_replicas(spec); set_stat_replicas(stat); } // // replica set // k8s_rs_t::k8s_rs_t(const std::string& name, const std::string& uid, const std::string& ns) : k8s_rc_t(name, uid, ns, COMPONENT_TYPE) { } // // service // k8s_service_t::k8s_service_t(const std::string& name, const std::string& uid, const std::string& ns) : k8s_component(COMPONENT_TYPE, name, uid, ns) { } std::vector k8s_service_t::get_selected_pods(const std::vector& pods) const { std::vector pod_vec; for(const auto& pod : pods) { if (selectors_in_labels(pod.get_labels()) && get_namespace() == pod.get_namespace()) { pod_vec.push_back(&pod); } } return pod_vec; } // // daemon set // k8s_daemonset_t::k8s_daemonset_t(const std::string& name, const std::string& uid, const std::string& ns) : k8s_component(COMPONENT_TYPE, name, uid, ns) { } // // deployment // k8s_deployment_t::k8s_deployment_t(const std::string& name, const std::string& uid, const std::string& ns) : k8s_component(COMPONENT_TYPE, name, uid, ns) { } std::vector k8s_deployment_t::get_selected_pods(const std::vector& pods) const { std::vector pod_vec; for(const auto& pod : pods) { if(selectors_in_labels(pod.get_labels()) && get_namespace() == pod.get_namespace()) { pod_vec.push_back(&pod); } } return pod_vec; } // // event // k8s_event_t::k8s_event_t(const std::string& name, const std::string& uid, const std::string& ns) : k8s_component(COMPONENT_TYPE, name, uid, ns), m_name_translation { // // Event translations, based on: // https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/container/event.go // https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/controller_utils.go // https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/node/nodecontroller.go // https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/kubelet.go // https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/daemon/controller.go // https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/deployment/deployment_controller.go // https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/deployment/util/deployment_util.go // // // Node // // Node Controller { "TerminatedAllPods", "Terminated All Pods"}, { "RegisteredNode", "Node Registered"}, { "RemovingNode", "Removing Node"}, { "DeletingNode", "Deleting Node"}, { "DeletingAllPods", "Deleting All Pods"}, { "TerminatingEvictedPod", "Terminating Evicted Pod" }, // Kubelet { "NodeReady", "Node Ready" }, { "NodeNotReady", "Node not Ready" }, { "NodeSchedulable", "Node is Schedulable" }, { "NodeNotSchedulable", "Node is not Schedulable" }, { "CIDRNotAvailable", "CIDR not Available" }, { "CIDRAssignmentFailed", "CIDR Assignment Failed" }, { "Starting", "Starting Kubelet" }, { "KubeletSetupFailed", "Kubelet Setup Failed" }, { "FailedMount", "Volume Mount Failed" }, { "NodeSelectorMismatching", "Node Selector Mismatch" }, { "InsufficientFreeCPU", "Insufficient Free CPU" }, { "InsufficientFreeMemory", "Insufficient Free Memory" }, { "OutOfDisk", "Out of Disk" }, { "HostNetworkNotSupported", "Host Network not Supported" }, { "NilShaper", "Undefined Shaper" }, { "Rebooted", "Node Rebooted" }, { "NodeHasSufficientDisk", "Node Has Sufficient Disk" }, { "NodeOutOfDisk", "Node Out of Disk Space" }, // Image manager { "InvalidDiskCapacity", "Invalid Disk Capacity" }, { "FreeDiskSpaceFailed", "Free Disk Space Failed" }, // // Pod // // Image { "Pulling", "Pulling Image" }, { "Pulled", "Image Pulled" }, { "Failed", "Container Image Pull, Create or Start Failed" }, { "InspectFailed", "Image Inspect Failed" }, { "ErrImageNeverPull", "Image NeverPull Policy Error" }, { "BackOff", "Back Off Container Start or Image Pull" }, //{ "OutOfDisk" ,"Out of Disk" }, duplicate // Container { "Created", "Container Created" }, { "Started", "Container Started" }, //{ "Failed", "Container Create or Start Failed" }, duplicate { "Killing", "Killing Container" }, //{ "BackOff", "Backoff Start Container" }, duplicate // Probe { "Unhealthy", "Container Unhealthy" }, // Pod worker { "FailedSync", "Pod Sync Failed" }, // Config { "FailedValidation", "Failed Configuration Validation" }, { "HostPortConflict", "Host/Port Conflict" }, // // Replication Controller // { "SuccessfulCreate", "Pod Created" }, { "FailedCreate", "Pod Create Failed"}, { "SuccessfulDelete", "Pod Deleted" }, { "FailedDelete", "Pod Delete Failed"}, // // Replica Set // // { "SuccessfulCreate", "Pod Created" }, duplicate // { "FailedCreate", "Pod Create Failed"}, duplicate // { "SuccessfulDelete", "Pod Deleted" }, duplicate // { "FailedDelete", "Pod Delete Failed"} duplicate // // Deployment // { "SelectingAll", "Selecting All Pods" }, { "ScalingReplicaSet", "Scaling Replica Set" }, { "DeploymentRollbackRevisionNotFound", "No revision to roll back" }, { "DeploymentRollbackTemplateUnchanged", "Skipping Rollback" }, { "DeploymentRollback", "Rollback Done" } // // Daemon Set // // { "SelectingAll", "Selecting All Pods" } duplicate } { } void k8s_event_t::post_process(k8s_state_t& state) { for(auto it = m_postponed_events.cbegin(); it != m_postponed_events.end();) { g_logger.log("K8s event: " + std::to_string(m_postponed_events.size()) + " postponed events. " "post-processing event [" + it->first + "] ...", sinsp_logger::SEV_TRACE); m_force_delete = false; bool updated = update(it->second, state); if(updated || m_force_delete) { g_logger.log("K8s event: event [" + it->first + "] post-processed.", sinsp_logger::SEV_TRACE); m_postponed_events.erase(it++); } else { g_logger.log("K8s event: event [" + it->first + "] not post-processed. There's " + std::to_string(m_postponed_events.size()) + " postponed events pending.", sinsp_logger::SEV_TRACE); ++it; } } } bool k8s_event_t::update(const Json::Value& item, k8s_state_t& state) { #ifndef _WIN32 time_t epoch_time_evt_s = 0; time_t epoch_time_now_s = get_epoch_utc_seconds_now(); std::string event_name; std::string description; severity_t severity = user_event_logger::SEV_EVT_INFORMATION; event_scope scope; tag_map_t tags; const Json::Value& obj = item["involvedObject"]; if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log("K8s EVENT: \n" + json_as_string(item), sinsp_logger::SEV_TRACE); } if(!obj.isNull()) { std::string sev = get_json_string(item, "type"); // currently, only "Normal" and "Warning" severity = user_event_logger::SEV_EVT_INFORMATION; if(sev == "Warning") { severity = user_event_logger::SEV_EVT_WARNING; } if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log("K8s EVENT:" "\nnamespace = " + get_json_string(obj, "namespace") + "\nname = " + get_json_string(obj, "name") + "\nuid = " + get_json_string(obj, "uid") + "\ntype = " + get_json_string(obj, "kind") + "\nseverity = " + get_json_string(item, "type") + " (" + std::to_string(severity) + ')', sinsp_logger::SEV_TRACE); } } else { g_logger.log("K8s event: cannot get involved object (null)", sinsp_logger::SEV_ERROR); m_force_delete = true; return false; } std::string ts = get_json_string(item , "lastTimestamp"); if(!ts.empty()) { if((epoch_time_evt_s = get_epoch_utc_seconds(ts)) == (time_t) -1) { g_logger.log("K8s event: cannot convert [" + ts + "] to epoch timestamp", sinsp_logger::SEV_ERROR); } g_logger.log("K8s EVENT update: time:" + std::to_string(epoch_time_evt_s), sinsp_logger::SEV_DEBUG); } else { g_logger.log("K8s event: cannot convert time (null, empty or not string)", sinsp_logger::SEV_ERROR); } event_name = get_json_string(item , "reason"); const auto& translation = m_name_translation.find(event_name); if(translation != m_name_translation.end()) { event_name = translation->second; } description = get_json_string(item, "message"); g_logger.log("K8s EVENT message:" + description, sinsp_logger::SEV_DEBUG); // Although it's easier and more efficient to obtain the involved object data from // the event itself, there is a downside - event may not carry the data in the // same format as reported in metadata protobuf (generated from k8s state); // an example is IP address vs. DNS name for node, there may be other cases. // For that reason, we try to obtain info about involved object from state; if object is // not found in state (due to undefined arrival order of event and metadata messages), // we get scope data from the event itself. std::string component_uid = get_json_string(obj, "uid"); g_logger.log("K8s event UID:" + component_uid, sinsp_logger::SEV_TRACE); if(!component_uid.empty()) { g_logger.log("K8s event: seconds since event occurred:" + std::to_string(epoch_time_now_s - epoch_time_evt_s), sinsp_logger::SEV_TRACE); std::string t; const k8s_component* comp = state.get_component(component_uid, &t); if(comp && !t.empty()) { const std::string& node_name = comp->get_node_name(); if(!node_name.empty()) { scope.add("kubernetes.node.name", node_name); } const std::string& ns = comp->get_namespace(); if(!ns.empty()) { scope.add("kubernetes.namespace.name", ns); } const std::string& comp_name = comp->get_name(); if(!comp_name.empty()) { scope.add(std::string("kubernetes.").append(t).append(".name"), comp_name); } /* no labels for now for(const auto& label : comp->get_labels()) { tags[label.first] = label.second; //g_logger.log("EVENT label: [" + label.first + ':' + label.second + ']', sinsp_logger::SEV_DEBUG); if(event_scope::check(label.second)) { scope.append(" and kubernetes.").append(t).append(".label.").append(label.first).append(1, '=').append(label.second); } else { g_logger.log("K8s invalid scope entry: [" + label.second + ']', sinsp_logger::SEV_WARNING); } }*/ } else { g_logger.log("K8s event: cannot obtain component (component with UID [" + component_uid + "] not found), trying to build scope directly from event ...", sinsp_logger::SEV_TRACE); make_scope(obj, scope); } } else { g_logger.log("K8s event: cannot obtain component UID, trying to build scope directly from event ...", sinsp_logger::SEV_TRACE); make_scope(obj, scope); } tags["source"] = "kubernetes"; auto evt = sinsp_user_event(epoch_time_evt_s, std::move(event_name), std::move(description), std::move(scope.get_ref()), std::move(tags), severity); user_event_logger::log(evt, severity); // TODO: sysdig capture? #endif // _WIN32 return true; } void k8s_event_t::make_scope_impl(const Json::Value& obj, std::string comp, event_scope& scope, bool ns) { if(ns) { const std::string& ns_name = get_json_string(obj, "namespace"); if(!ns_name.empty()) { scope.add("kubernetes.namespace.name", ns_name); } } if(comp.length() && ci_compare::is_equal(get_json_string(obj, "kind"), comp)) { const std::string& comp_name = get_json_string(obj, "name"); if(!comp_name.empty()) { comp[0] = tolower(comp[0]); scope.add(std::string("kubernetes.").append(comp).append(".name"), comp_name); } if(comp_name.empty()) { g_logger.log("K8s " + comp + " event detected but " + comp + " name could not be determined. Scope will be empty.", sinsp_logger::SEV_WARNING); } } else { g_logger.log("K8s event detected but component name was empty. Scope will be empty.", sinsp_logger::SEV_WARNING); } } void k8s_event_t::make_scope(const Json::Value& obj, event_scope& scope) { if(ci_compare::is_equal(get_json_string(obj, "kind"), "Pod")) { make_scope_impl(obj, "Pod", scope); } else if(ci_compare::is_equal(get_json_string(obj, "kind"), "ReplicationController")) { make_scope_impl(obj, "ReplicationController", scope); } else if(ci_compare::is_equal(get_json_string(obj, "kind"), "Node")) { make_scope_impl(obj, "Node", scope, false); } else if(ci_compare::is_equal(get_json_string(obj, "kind"), "ReplicaSet")) { make_scope_impl(obj, "ReplicaSet", scope); } else if(ci_compare::is_equal(get_json_string(obj, "kind"), "Deployment")) { make_scope_impl(obj, "Deployment", scope); } else if(ci_compare::is_equal(get_json_string(obj, "kind"), "DaemonSet")) { make_scope_impl(obj, "DaemonSet", scope); } } sysdig-0.26.4/userspace/libsinsp/k8s_component.h000066400000000000000000000556501352731327100217010ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_component.h // // kubernetes components (nodes, namespaces, pods, replication controllers, services) // abstraction // #pragma once #include "json/json.h" #include "sinsp.h" #include "sinsp_int.h" #include "logger.h" #include "user_event.h" #include "user_event_logger.h" #include #include typedef std::pair k8s_pair_t; typedef std::vector k8s_pair_list; class k8s_pod_t; class k8s_service_t; class k8s_container { public: typedef std::vector list; class port { public: void set_name(const std::string& name); const std::string& get_name() const; void set_port(uint32_t port); uint32_t get_port() const; void set_protocol(const std::string& protocol); const std::string& get_protocol() const; bool operator==(const port& other) const; bool operator!=(const port& other) const; private: std::string m_name; uint32_t m_port = 0; std::string m_protocol; }; typedef std::vector port_list; k8s_container(); k8s_container(const std::string& name, const port_list& ports); k8s_container(const k8s_container& other); k8s_container(k8s_container&& other); k8s_container& operator=(const k8s_container& other); bool has_port(const std::string& port_name) const; const port* get_port(const std::string& port_name) const; void set_name(const std::string& name); const std::string& get_name() const; bool operator==(const k8s_container& other) const; bool operator!=(const k8s_container& other) const; private: std::string m_name; port_list m_ports; }; // // component // class k8s_component { public: enum type { K8S_NODES, K8S_NAMESPACES, K8S_PODS, K8S_REPLICATIONCONTROLLERS, K8S_SERVICES, K8S_EVENTS, K8S_REPLICASETS, K8S_DAEMONSETS, K8S_DEPLOYMENTS, K8S_COMPONENT_COUNT }; typedef std::set ext_list_t; typedef std::shared_ptr ext_list_ptr_t; typedef std::pair component_pair; typedef std::map type_map; static const type_map list; enum msg_reason { COMPONENT_ADDED, COMPONENT_MODIFIED, COMPONENT_DELETED, COMPONENT_ERROR, COMPONENT_NONEXISTENT, COMPONENT_UNKNOWN // only to mark bad event messages }; struct msg_data { msg_reason m_reason = COMPONENT_UNKNOWN; std::string m_name; std::string m_uid; std::string m_namespace; std::string m_kind; bool is_valid() const { return m_reason != COMPONENT_UNKNOWN; } std::string get_reason_desc() const { switch(m_reason) { case COMPONENT_ADDED: return "ADDED"; case COMPONENT_MODIFIED: return "MODIFIED"; case COMPONENT_DELETED: return "DELETED"; case COMPONENT_ERROR: return "ERROR"; case COMPONENT_NONEXISTENT: return "NONEXISTENT"; case COMPONENT_UNKNOWN: default: return "UNKNOWN"; } return "UNKNOWN"; } }; k8s_component() = delete; k8s_component(type comp_type, const std::string& name, const std::string& uid, const std::string& ns = ""); virtual ~k8s_component(); const std::string& get_name() const; void set_name(const std::string& name); const std::string& get_uid() const; void set_uid(const std::string& uid); const std::string& get_namespace() const; void set_namespace(const std::string& ns); k8s_pair_t* get_label(const k8s_pair_t& label); const k8s_pair_list& get_labels() const; void set_labels(k8s_pair_list&& labels); void add_labels(k8s_pair_list&& labels); void swap_labels(k8s_pair_list& new_labels); void push_label(const k8s_pair_t& label); void emplace_label(const k8s_pair_t& label); k8s_pair_t* get_selector(const k8s_pair_t& selector); const k8s_pair_list& get_selectors() const; void set_selectors(k8s_pair_list&& selectors); void add_selectors(k8s_pair_list&& selectors); void swap_selectors(k8s_pair_list& new_selectors); void push_selector(const k8s_pair_t& selector); void emplace_selector(k8s_pair_t&& selector); virtual std::string get_node_name() const; template static void extract_string_array(const Json::Value& arr, C& list) { if(!arr.isNull() && arr.isArray()) { for (auto& item : arr) { if(item.isConvertibleTo(Json::stringValue)) { list.emplace(item.asString()); } } } } static k8s_pair_list extract_object(const Json::Value& object, const std::string& name); static const std::string& get_name(const component_pair& p); static std::string get_name(type t); static std::string get_name_u(type t); static type get_type(const component_pair& p); static type get_type(const std::string& name); static std::string get_api(type t, ext_list_ptr_t extensions = nullptr); static std::string get_api(const component_pair& p, ext_list_ptr_t extensions = nullptr); static std::string get_api(const std::string& name, ext_list_ptr_t extensions = nullptr); static std::string get_selector(type t); static std::string get_selector(const component_pair& p); static std::string get_selector(const std::string& name); static bool is_critical(type t); static bool is_critical(const component_pair& p); static bool is_critical(const std::string& name); bool selector_in_labels(const k8s_pair_t& selector, const k8s_pair_list& labels) const; bool selectors_in_labels(const k8s_pair_list& labels) const; private: type m_type; std::string m_name; std::string m_uid; std::string m_ns; k8s_pair_list m_labels; k8s_pair_list m_selectors; friend class k8s_state_t; friend class k8s_dispatcher; }; // // namespace // class k8s_ns_t : public k8s_component { public: static const k8s_component::type COMPONENT_TYPE = K8S_NAMESPACES; k8s_ns_t(const std::string& name, const std::string& uid, const std::string& ns = ""); }; // // node // class k8s_node_t : public k8s_component { public: typedef std::unordered_set host_ip_list; static const k8s_component::type COMPONENT_TYPE = K8S_NODES; k8s_node_t(const std::string& name, const std::string& uid, const std::string& ns = ""); const host_ip_list& get_host_ips() const; void set_host_ips(host_ip_list&& host_ips); void add_host_ips(host_ip_list&& host_ips); void emplace_host_ip(std::string&& host_ip); virtual std::string get_node_name() const; static host_ip_list extract_addresses(const Json::Value& status); private: host_ip_list m_host_ips; }; // // pod // class k8s_pod_t : public k8s_component { public: typedef std::vector container_id_list; typedef k8s_container::list container_list; static const k8s_component::type COMPONENT_TYPE = K8S_PODS; k8s_pod_t(const std::string& name, const std::string& uid, const std::string& ns = ""); // container IDs const container_id_list& get_container_ids() const; void set_container_ids(container_id_list&& container_ids); void add_container_ids(container_id_list&& container_ids); void push_container_id(const std::string& container_id); void emplace_container_id(std::string&& container_id); // restart count size_t get_restart_count() const; void set_restart_count(int rc); // containers const container_list& get_containers() const; void set_containers(container_list&& containers); void add_containers(container_list&& containers); void push_container(const k8s_container& container); void emplace_container(k8s_container&& container); // node name, host IP and internal IP virtual std::string get_node_name() const; void set_node_name(const std::string& name); const std::string& get_host_ip() const; void set_host_ip(const std::string& host_ip); const std::string& get_internal_ip() const; void set_internal_ip(const std::string& internal_ip); // comparison bool operator==(const k8s_pod_t& other) const; bool operator!=(const k8s_pod_t& other) const; private: container_id_list m_container_ids; container_list m_containers; std::string m_node_name; std::string m_host_ip; std::string m_internal_ip; int m_restart_count_tot = -1; mutable int m_restart_count_diff = 0; }; // // replicas // class k8s_replicas_t { public: static const int UNKNOWN_REPLICAS = -1; k8s_replicas_t(int spec_replicas = UNKNOWN_REPLICAS, int stat_replicas = UNKNOWN_REPLICAS); void set_spec_replicas(int replicas); int get_spec_replicas() const; void set_stat_replicas(int replicas); int get_stat_replicas() const; static int get_count(const Json::Value& item, const std::string& replica_name = "replicas"); static void set_replicas(k8s_replicas_t& replicas, const Json::Value& item); protected: int m_spec_replicas = UNKNOWN_REPLICAS; int m_stat_replicas = UNKNOWN_REPLICAS; }; // // replication controller // class k8s_rc_t : public k8s_component { public: static const k8s_component::type COMPONENT_TYPE = K8S_REPLICATIONCONTROLLERS; k8s_rc_t(const std::string& name, const std::string& uid, const std::string& ns = "", k8s_component::type type = K8S_REPLICATIONCONTROLLERS); std::vector get_selected_pods(const std::vector& pods) const; void set_spec_replicas(int replicas); int get_spec_replicas() const; void set_stat_replicas(int replicas); int get_stat_replicas() const; void set_replicas(const Json::Value& item, const std::string& replica_name = "replicas"); void set_replicas(int spec, int stat); protected: k8s_replicas_t m_replicas; }; // // replica set // class k8s_rs_t : public k8s_rc_t { public: static const k8s_component::type COMPONENT_TYPE = K8S_REPLICASETS; k8s_rs_t(const std::string& name, const std::string& uid, const std::string& ns = ""); private: }; // // service // class k8s_service_t : public k8s_component { public: struct net_port { uint32_t m_port = 0; std::string m_protocol; uint32_t m_target_port = 0; uint32_t m_node_port = 0; }; typedef std::vector port_list; static const k8s_component::type COMPONENT_TYPE = K8S_SERVICES; k8s_service_t(const std::string& name, const std::string& uid, const std::string& ns = ""); const std::string& get_cluster_ip() const; void set_cluster_ip(const std::string& cluster_ip); const port_list& get_port_list() const; void set_port_list(port_list&& ports); std::vector get_selected_pods(const std::vector& pods) const; private: std::string m_cluster_ip; port_list m_ports; }; // // daemon set // class k8s_daemonset_t : public k8s_component { public: static const k8s_component::type COMPONENT_TYPE = K8S_DAEMONSETS; k8s_daemonset_t(const std::string& name, const std::string& uid, const std::string& ns = ""); void set_desired_scheduled(int replicas); int get_desired_scheduled() const; void set_current_scheduled(int replicas); int get_current_scheduled() const; void set_scheduled(const Json::Value& item); void set_scheduled(int desired, int current); private: k8s_replicas_t m_replicas; }; // // deployment // class k8s_deployment_t : public k8s_component { public: static const k8s_component::type COMPONENT_TYPE = K8S_DEPLOYMENTS; k8s_deployment_t(const std::string& name, const std::string& uid, const std::string& ns = ""); void set_spec_replicas(int replicas); int get_spec_replicas() const; void set_stat_replicas(int replicas); int get_stat_replicas() const; void set_replicas(const Json::Value& item); void set_replicas(int desired, int current); std::vector get_selected_pods(const std::vector& pods) const; private: k8s_replicas_t m_replicas; }; // // event // class k8s_state_t; class event_scope; class k8s_event_t : public k8s_component { public: static const k8s_component::type COMPONENT_TYPE = K8S_EVENTS; k8s_event_t(const std::string& name, const std::string& uid, const std::string& ns); bool update(const Json::Value& item, k8s_state_t& state); void post_process(k8s_state_t& state); bool has_pending_events() const; private: typedef sinsp_user_event::tag_map_t tag_map_t; typedef user_event_logger::severity severity_t; typedef std::unordered_map name_translation_map_t; void make_scope(const Json::Value& obj, event_scope& scope); void make_scope_impl(const Json::Value& obj, std::string comp, event_scope& scope, bool ns = true); name_translation_map_t m_name_translation; std::map m_postponed_events; bool m_force_delete = false; }; typedef std::vector k8s_namespaces; typedef std::vector k8s_nodes; typedef std::vector k8s_pods; typedef std::vector k8s_controllers; typedef std::vector k8s_replicasets; typedef std::vector k8s_services; typedef std::vector k8s_daemonsets; typedef std::vector k8s_deployments; typedef std::vector k8s_events; // // container // inline const std::string& k8s_container::get_name() const { return m_name; } inline void k8s_container::set_name(const std::string& name) { m_name = name; } inline bool k8s_container::operator==(const k8s_container& other) const { if(&other == this) { return true; } return (other.m_name == m_name) && (other.m_ports == m_ports); } inline bool k8s_container::operator!=(const k8s_container& other) const { if(&other == this) { return false; } return !(other == *this); } // // container::port // inline void k8s_container::port::set_name(const std::string& name) { m_name = name; } inline const std::string& k8s_container::port::get_name() const { return m_name; } inline void k8s_container::port::set_port(uint32_t port) { m_port = port; } inline uint32_t k8s_container::port::get_port() const { return m_port; } inline void k8s_container::port::set_protocol(const std::string& protocol) { m_protocol = protocol; } inline const std::string& k8s_container::port::get_protocol() const { return m_protocol; } inline bool k8s_container::port::operator==(const port& other) const { if(&other == this) { return true; } return other.m_name == m_name && other.m_port == m_port && other.m_protocol == m_protocol; } inline bool k8s_container::port::operator!=(const port& other) const { if(&other == this) { return false; } return !(other == *this); } // // component // inline const std::string& k8s_component::get_name() const { return m_name; } inline void k8s_component::set_name(const std::string& name) { m_name = name; } inline const std::string& k8s_component::get_uid() const{ return m_uid; } inline void k8s_component::set_uid(const std::string& uid) { m_uid = uid; } inline const std::string& k8s_component::get_namespace() const { return m_ns; } inline void k8s_component::set_namespace(const std::string& ns) { m_ns = ns; } inline const k8s_pair_list& k8s_component::get_labels() const { return m_labels; } inline void k8s_component::set_labels(k8s_pair_list&& labels) { m_labels = std::move(labels); } inline void k8s_component::swap_labels(k8s_pair_list& new_labels) { m_labels.swap(new_labels); } inline void k8s_component::push_label(const k8s_pair_t& label) { m_labels.push_back(label); } inline void k8s_component::emplace_label(const k8s_pair_t& label) { m_labels.emplace_back(label); } inline const k8s_pair_list& k8s_component::get_selectors() const { return m_selectors; } inline void k8s_component::set_selectors(k8s_pair_list&& selectors) { m_selectors = std::move(selectors); } inline void k8s_component::swap_selectors(k8s_pair_list& new_selectors) { m_selectors.swap(new_selectors); } inline void k8s_component::push_selector(const k8s_pair_t& selector) { m_selectors.push_back(selector); } inline void k8s_component::emplace_selector(k8s_pair_t&& selector) { m_selectors.emplace_back(std::move(selector)); } inline const std::string& k8s_component::get_name(const component_pair& p) { return p.second; } inline k8s_component::type k8s_component::get_type(const component_pair& p) { return p.first; } inline std::string k8s_component::get_node_name() const { return ""; } // // node // inline const k8s_node_t::host_ip_list& k8s_node_t::get_host_ips() const { return m_host_ips; } inline void k8s_node_t::set_host_ips(host_ip_list&& host_ips) { m_host_ips = std::move(host_ips); } inline void k8s_node_t::add_host_ips(host_ip_list&& host_ips) { m_host_ips.insert(host_ips.begin(), host_ips.end()); } inline void k8s_node_t::emplace_host_ip(std::string&& host_ip) { m_host_ips.emplace(std::move(host_ip)); } inline std::string k8s_node_t::get_node_name() const { return get_name(); } // // pod // // container IDs inline const k8s_pod_t::container_id_list& k8s_pod_t::get_container_ids() const { return m_container_ids; } inline void k8s_pod_t::set_container_ids(container_id_list&& container_ids) { m_container_ids = std::move(container_ids); } inline void k8s_pod_t::add_container_ids(container_id_list&& container_ids) { m_container_ids.insert(m_container_ids.end(), container_ids.begin(), container_ids.end()); } inline void k8s_pod_t::push_container_id(const std::string& container_id) { m_container_ids.push_back(container_id); } inline void k8s_pod_t::emplace_container_id(std::string&& container_id) { m_container_ids.emplace_back(std::move(container_id)); } // restart count inline size_t k8s_pod_t::get_restart_count() const { int restart_count_diff = m_restart_count_diff; m_restart_count_diff = 0; return restart_count_diff; } inline void k8s_pod_t::set_restart_count(int rc) { if(rc < 0) { g_logger.log("Unexpected K8S pod restart count received: " + std::to_string(rc), sinsp_logger::SEV_WARNING); return; } // only record current total on first call if(m_restart_count_tot == -1) { m_restart_count_tot = rc; return; } if(rc >= m_restart_count_tot) { m_restart_count_diff = rc - m_restart_count_tot; } else { g_logger.log("Unexpected K8S pod restart count received (" + std::to_string(rc) + ", last recorded value " + std::to_string(m_restart_count_tot) + "), resetting diff to zero.", sinsp_logger::SEV_WARNING); m_restart_count_diff = 0; } m_restart_count_tot = rc; } // comparison inline bool k8s_pod_t::operator==(const k8s_pod_t& other) const { if(&other == this) { return true; } return other.m_container_ids == m_container_ids && other.m_containers == m_containers && other.m_host_ip == m_host_ip && other.m_internal_ip == m_internal_ip; } inline bool k8s_pod_t::operator!=(const k8s_pod_t& other) const { if(&other == this) { return false; } return !(other == *this); } // containers inline const k8s_pod_t::container_list& k8s_pod_t::get_containers() const { return m_containers; } inline void k8s_pod_t::set_containers(container_list&& containers) { m_containers = std::move(containers); } inline void k8s_pod_t::add_containers(container_list&& containers) { m_containers.insert(m_containers.end(), containers.begin(), containers.end()); } inline void k8s_pod_t::push_container(const k8s_container& container) { m_containers.push_back(container); } inline void k8s_pod_t::emplace_container(k8s_container&& container) { m_containers.emplace_back(std::move(container)); } // getters/setters inline std::string k8s_pod_t::get_node_name() const { return m_node_name; } inline void k8s_pod_t::set_node_name(const std::string& name) { m_node_name = name; } inline const std::string& k8s_pod_t::get_host_ip() const { return m_host_ip; } inline void k8s_pod_t::set_host_ip(const std::string& host_ip) { m_host_ip = host_ip; } inline const std::string& k8s_pod_t::get_internal_ip() const { return m_internal_ip; } inline void k8s_pod_t::set_internal_ip(const std::string& internal_ip) { m_internal_ip = internal_ip; } // // replicas // inline void k8s_replicas_t::set_spec_replicas(int replicas) { m_spec_replicas = replicas; } inline int k8s_replicas_t::get_spec_replicas() const { return m_spec_replicas; } inline void k8s_replicas_t::set_stat_replicas(int replicas) { m_stat_replicas = replicas; } inline int k8s_replicas_t::get_stat_replicas() const { return m_stat_replicas; } // // replication controller // inline void k8s_rc_t::set_spec_replicas(int replicas) { m_replicas.set_spec_replicas(replicas); } inline int k8s_rc_t::get_spec_replicas() const { return m_replicas.get_spec_replicas(); } inline void k8s_rc_t::set_stat_replicas(int replicas) { m_replicas.set_stat_replicas(replicas); } inline int k8s_rc_t::get_stat_replicas() const { return m_replicas.get_stat_replicas(); } inline void k8s_rc_t::set_replicas(const Json::Value& item, const std::string& replica_name) { k8s_replicas_t::set_replicas(m_replicas, item); } // // service // inline const std::string& k8s_service_t::get_cluster_ip() const { return m_cluster_ip; } inline void k8s_service_t::set_cluster_ip(const std::string& cluster_ip) { m_cluster_ip = cluster_ip; } inline const k8s_service_t::port_list& k8s_service_t::get_port_list() const { return m_ports; } inline void k8s_service_t::set_port_list(port_list&& ports) { m_ports = std::move(ports); } // // deployment // inline void k8s_deployment_t::set_spec_replicas(int replicas) { m_replicas.set_spec_replicas(replicas); } inline int k8s_deployment_t::get_spec_replicas() const { return m_replicas.get_spec_replicas(); } inline void k8s_deployment_t::set_stat_replicas(int replicas) { m_replicas.set_stat_replicas(replicas); } inline int k8s_deployment_t::get_stat_replicas() const { return m_replicas.get_stat_replicas(); } inline void k8s_deployment_t::set_replicas(const Json::Value& item) { k8s_replicas_t::set_replicas(m_replicas, item); } inline void k8s_deployment_t::set_replicas(int desired, int current) { m_replicas.set_spec_replicas(desired); m_replicas.set_stat_replicas(current); } // // daemon set // inline void k8s_daemonset_t::set_desired_scheduled(int scheduled) { m_replicas.set_spec_replicas(scheduled); } inline int k8s_daemonset_t::get_desired_scheduled() const { return m_replicas.get_spec_replicas(); } inline void k8s_daemonset_t::set_current_scheduled(int scheduled) { m_replicas.set_stat_replicas(scheduled); } inline int k8s_daemonset_t::get_current_scheduled() const { return m_replicas.get_stat_replicas(); } inline void k8s_daemonset_t::set_scheduled(const Json::Value& item) { m_replicas.set_spec_replicas(k8s_replicas_t::get_count(item["status"], "desiredNumberScheduled")); m_replicas.set_stat_replicas(k8s_replicas_t::get_count(item["status"], "currentNumberScheduled")); } inline void k8s_daemonset_t::set_scheduled(int desired, int current) { m_replicas.set_spec_replicas(desired); m_replicas.set_stat_replicas(current); } // // event // inline bool k8s_event_t::has_pending_events() const { return m_postponed_events.size() != 0; } sysdig-0.26.4/userspace/libsinsp/k8s_daemonset_handler.cpp000066400000000000000000000104151352731327100236740ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_daemonset_handler.cpp // #ifndef CYGWING_AGENT #include "k8s_daemonset_handler.h" #include "sinsp.h" #include "sinsp_int.h" // filters normalize state and event JSONs, so they can be processed generically: // event is turned into a single-entry array, state is turned into an array of ADDED events std::string k8s_daemonset_handler::EVENT_FILTER = "{" " type: .type," " apiVersion: .object.apiVersion," " kind: .object.kind," " items:" " [" " .object |" " {" " namespace: .metadata.namespace," " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " desiredScheduled: .status.desiredNumberScheduled," " currentScheduled: .status.currentNumberScheduled," " selector: .spec.selector.matchLabels," " labels: .metadata.labels" " }" " ]" "}"; std::string k8s_daemonset_handler::STATE_FILTER = "{" " type: \"ADDED\"," " apiVersion: .apiVersion," " kind: \"DaemonSet\", " " items:" " [" " .items[] | " " {" " namespace: .metadata.namespace," " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " desiredScheduled: .status.desiredNumberScheduled," " currentScheduled: .status.currentNumberScheduled," " selector: .spec.selector.matchLabels," " labels: .metadata.labels" " }" " ]" "}"; std::string k8s_daemonset_handler::NULL_FILTER = "{" " type: \"NONEXISTENT\"," " apiVersion: .apiVersion," " kind: \"DaemonSet\", " " items: [ null ]" "}"; k8s_daemonset_handler::k8s_daemonset_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector ,std::string url ,const std::string& http_version ,ssl_ptr_t ssl ,bt_ptr_t bt ,bool connect ,bool blocking_socket #endif // HAS_CAPTURE ): k8s_handler("k8s_daemonset_handler", true, #ifdef HAS_CAPTURE url, "/apis/extensions/v1beta1/daemonsets", STATE_FILTER, EVENT_FILTER, NULL_FILTER, collector, http_version, 1000L, ssl, bt, true, connect, dependency_handler, blocking_socket, #endif // HAS_CAPTURE 100, // max msgs &state) { } k8s_daemonset_handler::~k8s_daemonset_handler() { } bool k8s_daemonset_handler::handle_component(const Json::Value& json, const msg_data* data) { if(data) { if(m_state) { if((data->m_reason == k8s_component::COMPONENT_ADDED) || (data->m_reason == k8s_component::COMPONENT_MODIFIED)) { k8s_daemonset_t& ds = m_state->get_component(m_state->get_daemonsets(), data->m_name, data->m_uid, data->m_namespace); k8s_pair_list entries = extract_object(json["labels"]); if(entries.size() > 0) { ds.set_labels(std::move(entries)); } handle_selectors(ds, json["selector"]); const Json::Value& desired = json["desiredScheduled"]; const Json::Value& current = json["currentScheduled"]; if(!desired.isNull() && desired.isConvertibleTo(Json::intValue) && !current.isNull() && current.isConvertibleTo(Json::intValue)) { ds.set_scheduled(desired.asInt(), current.asInt()); } } else if(data->m_reason == k8s_component::COMPONENT_DELETED) { if(!m_state->delete_component(m_state->get_daemonsets(), data->m_uid)) { log_not_found(*data); return false; } } else if(data->m_reason != k8s_component::COMPONENT_ERROR) { g_logger.log(std::string("Unsupported K8S " + name() + " event reason: ") + std::to_string(data->m_reason), sinsp_logger::SEV_ERROR); return false; } } else { throw sinsp_exception("K8s node handler: state is null."); } } else { throw sinsp_exception("K8s node handler: data is null."); } return true; } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/k8s_daemonset_handler.h000066400000000000000000000025301352731327100233400ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_daemonset_handler.h // #pragma once #include "json/json.h" #include "sinsp_auth.h" #include "k8s_handler.h" #include "k8s_state.h" class sinsp; class k8s_daemonset_handler : public k8s_handler { public: k8s_daemonset_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector = nullptr ,std::string url = "" ,const std::string& http_version = "1.1" ,ssl_ptr_t ssl = 0 ,bt_ptr_t bt = 0 ,bool connect = true ,bool blocking_socket = false #endif // HAS_CAPTURE ); ~k8s_daemonset_handler(); private: static std::string EVENT_FILTER; static std::string STATE_FILTER; static std::string NULL_FILTER; virtual bool handle_component(const Json::Value& json, const msg_data* data = 0); }; sysdig-0.26.4/userspace/libsinsp/k8s_deployment_handler.cpp000066400000000000000000000103351352731327100240760ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_deployment_handler.cpp // #ifndef CYGWING_AGENT #include "k8s_deployment_handler.h" #include "sinsp.h" #include "sinsp_int.h" // filters normalize state and event JSONs, so they can be processed generically: // event is turned into a single-entry array, state is turned into an array of ADDED events std::string k8s_deployment_handler::EVENT_FILTER = "{" " type: .type," " apiVersion: .object.apiVersion," " kind: .object.kind," " items:" " [" " .object |" " {" " namespace: .metadata.namespace," " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " specReplicas: .spec.replicas," " statReplicas: .status.replicas," " selector: .spec.selector.matchLabels," " labels: .metadata.labels" " }" " ]" "}"; std::string k8s_deployment_handler::STATE_FILTER = "{" " type: \"ADDED\"," " apiVersion: .apiVersion," " kind: \"Deployment\", " " items:" " [" " .items[] | " " {" " namespace: .metadata.namespace," " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " specReplicas: .spec.replicas," " statReplicas: .status.replicas," " selector: .spec.selector.matchLabels," " labels: .metadata.labels" " }" " ]" "}"; std::string k8s_deployment_handler::NULL_FILTER = "{" " type: \"NONEXISTENT\"," " apiVersion: .apiVersion," " kind: \"Deployment\", " " items: [ null ]" "}"; k8s_deployment_handler::k8s_deployment_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector ,std::string url ,const std::string& http_version ,ssl_ptr_t ssl ,bt_ptr_t bt ,bool connect ,bool blocking_socket #endif // HAS_CAPTURE ): k8s_handler("k8s_deployment_handler", true, #ifdef HAS_CAPTURE url, "/apis/extensions/v1beta1/deployments", STATE_FILTER, EVENT_FILTER, NULL_FILTER, collector, http_version, 1000L, ssl, bt, true, connect, dependency_handler, blocking_socket, #endif // HAS_CAPTURE 100, // max msgs &state) { } k8s_deployment_handler::~k8s_deployment_handler() { } bool k8s_deployment_handler::handle_component(const Json::Value& json, const msg_data* data) { if(data) { if(m_state) { if((data->m_reason == k8s_component::COMPONENT_ADDED) || (data->m_reason == k8s_component::COMPONENT_MODIFIED)) { k8s_deployment_t& deployment = m_state->get_component(m_state->get_deployments(), data->m_name, data->m_uid, data->m_namespace); k8s_pair_list entries = k8s_component::extract_object(json, "labels"); if(entries.size() > 0) { deployment.set_labels(std::move(entries)); } handle_selectors(deployment, json["selector"]); const Json::Value& spec = json["specReplicas"]; const Json::Value& stat = json["statReplicas"]; if(!spec.isNull() && spec.isConvertibleTo(Json::intValue) && !stat.isNull() && stat.isConvertibleTo(Json::intValue)) { deployment.set_replicas(spec.asInt(), stat.asInt()); } } else if(data->m_reason == k8s_component::COMPONENT_DELETED) { if(!m_state->delete_component(m_state->get_deployments(), data->m_uid)) { log_not_found(*data); return false; } } else if(data->m_reason != k8s_component::COMPONENT_ERROR) { g_logger.log(std::string("Unsupported K8S " + name() + " event reason: ") + std::to_string(data->m_reason), sinsp_logger::SEV_ERROR); return false; } } else { throw sinsp_exception("K8s node handler: state is null."); } } else { throw sinsp_exception("K8s node handler: data is null."); } return true; } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/k8s_deployment_handler.h000066400000000000000000000025351352731327100235460ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_deployment_handler.h // #pragma once #include "json/json.h" #include "sinsp_auth.h" #include "k8s_handler.h" #include "k8s_state.h" class sinsp; class k8s_deployment_handler : public k8s_handler { public: k8s_deployment_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector = nullptr ,std::string url = "" ,const std::string& http_version = "1.1" ,ssl_ptr_t ssl = 0 ,bt_ptr_t bt = 0 ,bool connect = true ,bool blocking_socket = false #endif // HAS_CAPTURE ); ~k8s_deployment_handler(); private: static std::string EVENT_FILTER; static std::string STATE_FILTER; static std::string NULL_FILTER; virtual bool handle_component(const Json::Value& json, const msg_data* data = 0); }; sysdig-0.26.4/userspace/libsinsp/k8s_dispatcher.cpp000066400000000000000000000553451352731327100223610ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_dispatcher.cpp // #ifndef CYGWING_AGENT #include "k8s_dispatcher.h" #include "k8s_service_handler.h" #include "sinsp.h" #include "sinsp_int.h" #include #include #include #include #include k8s_dispatcher::k8s_dispatcher(k8s_component::type t, k8s_state_t& state, filter_ptr_t event_filter): m_type(t), m_state(state), m_event_filter(event_filter) { } void k8s_dispatcher::enqueue(k8s_event_data&& event_data) { assert(event_data.component() == m_type); std::string data = event_data.data(); if(m_messages.size() == 0) { m_messages.push_back(""); } std::string* msg = &m_messages.back(); std::string::size_type pos = msg->find_first_of('\n'); // previous msg full, this is a beginning of new message if(pos != std::string::npos && pos == (msg->size() - 1)) { m_messages.push_back(""); msg = &m_messages.back(); } while ((pos = data.find_first_of('\n')) != std::string::npos) { msg->append(data.substr(0, pos + 1)); if(data.length() > pos + 1) { data = data.substr(pos + 1); m_messages.push_back(""); msg = &m_messages.back(); } else { break; } }; if(data.size() > 0) { msg->append(data); } dispatch(); // candidate for separate thread } bool k8s_dispatcher::is_valid(const std::string& msg) { // zero-length message is valid because that's how it starts its life. // so, here we only check for messages that are single newline only // or those that are longer than one character and contain multiple newlines. if((msg.size() == 1 && msg[0] == '\n') || std::count(msg.begin(), msg.end(), '\n') > 1) { return false; } return true; } bool k8s_dispatcher::is_ready(const std::string& msg) { // absurd minimum ( "{}\n" ) but it's hard to tell // what minimal size is, so there ... if(msg.size() < 3) { return false; } return msg[msg.size() - 1] == '\n'; } k8s_dispatcher::msg_data k8s_dispatcher::get_msg_data(Json::Value& root) { msg_data data; Json::Value evtype = root["type"]; if(!evtype.isNull() && evtype.isString()) { const std::string& et = evtype.asString(); if(!et.empty()) { if(et[0] == 'A') { data.m_reason = COMPONENT_ADDED; } else if(et[0] == 'M') { data.m_reason = COMPONENT_MODIFIED; } else if(et[0] == 'D') { data.m_reason = COMPONENT_DELETED; } else if(et[0] == 'E') { data.m_reason = COMPONENT_ERROR; } } else { return msg_data(); } } Json::Value object = root["object"]; // +++ for capture Json::Value kind = object["kind"]; if(!kind.isNull() && kind.isString() && root["kind"].isNull()) { root["kind"] = kind.asString(); } Json::Value api_version = object["apiVersion"]; if(!api_version.isNull() && api_version.isString() && root["apiVersion"].isNull()) { root["apiVersion"] = api_version.asString(); } // --- for capture if(!object.isNull() && object.isObject()) { Json::Value meta = object["metadata"]; if(!meta.isNull() && meta.isObject()) { Json::Value name = meta["name"]; if(!name.isNull()) { data.m_name = name.asString(); } Json::Value uid = meta["uid"]; if(!uid.isNull()) { data.m_uid = uid.asString(); } Json::Value nspace = meta["namespace"]; if(!nspace.isNull()) { data.m_namespace = nspace.asString(); } } } return data; } void k8s_dispatcher::log_error(const Json::Value& root, const std::string& comp) { std::string unk_err = "Unknown."; std::ostringstream os; os << "K8S server reported " << comp << " error: "; if(!root.isNull()) { Json::Value object = root["object"]; if(!object.isNull()) { os << object.toStyledString(); unk_err.clear(); } } os << unk_err; g_logger.log(os.str(), sinsp_logger::SEV_ERROR); } void k8s_dispatcher::handle_node(const Json::Value& root, const msg_data& data) { if(data.m_reason == COMPONENT_ADDED) { if(m_state.has(m_state.get_nodes(), data.m_uid)) { std::ostringstream os; os << "ADDED message received for existing node [" << data.m_uid << "], updating only."; g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); } k8s_node_t& node = m_state.get_component(m_state.get_nodes(), data.m_name, data.m_uid); const Json::Value& object = root["object"]; if(!object.isNull()) { const Json::Value& status = object["status"]; if(!status.isNull()) { k8s_node_t::host_ip_list addresses = k8s_node_t::extract_addresses(status); if(addresses.size() > 0) { node.set_host_ips(std::move(addresses)); } } Json::Value metadata = object["metadata"]; if(!metadata.isNull()) { k8s_pair_list entries = k8s_component::extract_object(metadata, "labels"); if(entries.size() > 0) { node.set_labels(std::move(entries)); } } } } else if(data.m_reason == COMPONENT_MODIFIED) { if(!m_state.has(m_state.get_nodes(), data.m_uid)) { std::ostringstream os; os << "MODIFIED message received for non-existing node [" << data.m_uid << "], giving up."; g_logger.log(os.str(), sinsp_logger::SEV_ERROR); return; } k8s_node_t& node = m_state.get_component(m_state.get_nodes(), data.m_name, data.m_uid); Json::Value object = root["object"]; if(!object.isNull()) { const Json::Value& status = object["status"]; if(!status.isNull()) { k8s_node_t::host_ip_list addresses = k8s_node_t::extract_addresses(status); if(addresses.size() > 0) { node.set_host_ips(std::move(addresses)); } } const Json::Value& metadata = object["metadata"]; if(!metadata.isNull()) { k8s_pair_list entries = k8s_component::extract_object(metadata, "labels"); if(entries.size() > 0) { node.add_labels(std::move(entries)); } } } } else if(data.m_reason == COMPONENT_DELETED) { if(!m_state.delete_component(m_state.get_nodes(), data.m_uid)) { g_logger.log(std::string("NODE not found: ") + data.m_name, sinsp_logger::SEV_ERROR); } } else if(data.m_reason == COMPONENT_ERROR) { log_error(root, "NODE"); } else { g_logger.log(std::string("Unsupported K8S NODE event reason: ") + std::to_string(data.m_reason), sinsp_logger::SEV_ERROR); } } void k8s_dispatcher::handle_namespace(const Json::Value& root, const msg_data& data) { if(data.m_reason == COMPONENT_ADDED) { if(m_state.has(m_state.get_namespaces(), data.m_uid)) { std::ostringstream os; os << "ADDED message received for existing namespace [" << data.m_uid << "], updating only."; g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); } k8s_ns_t& ns = m_state.get_component(m_state.get_namespaces(), data.m_name, data.m_uid); const Json::Value& object = root["object"]; if(!object.isNull()) { const Json::Value& metadata = object["metadata"]; if(!metadata.isNull()) { k8s_pair_list entries = k8s_component::extract_object(metadata, "labels"); if(entries.size() > 0) { ns.set_labels(std::move(entries)); } } } } else if(data.m_reason == COMPONENT_MODIFIED) { if(!m_state.has(m_state.get_namespaces(), data.m_uid)) { std::ostringstream os; os << "MODIFIED message received for non-existing namespace [" << data.m_uid << "], giving up."; g_logger.log(os.str(), sinsp_logger::SEV_ERROR); return; } k8s_ns_t& ns = m_state.get_component(m_state.get_namespaces(), data.m_name, data.m_uid); const Json::Value& object = root["object"]; if(!object.isNull()) { const Json::Value& metadata = object["metadata"]; if(!metadata.isNull()) { k8s_pair_list entries = k8s_component::extract_object(metadata, "labels"); if(entries.size() > 0) { ns.add_labels(std::move(entries)); } } } } else if(data.m_reason == COMPONENT_DELETED) { if(!m_state.delete_component(m_state.get_namespaces(), data.m_uid)) { g_logger.log(std::string("NAMESPACE not found: ") + data.m_name, sinsp_logger::SEV_ERROR); } } else if(data.m_reason == COMPONENT_ERROR) { log_error(root, "NAMESPACE"); } else { g_logger.log(std::string("Unsupported K8S NAMESPACE event reason: ") + std::to_string(data.m_reason), sinsp_logger::SEV_ERROR); } } bool k8s_dispatcher::handle_pod(const Json::Value& root, const msg_data& data) { if(data.m_reason == COMPONENT_ADDED) { const Json::Value& object = root["object"]; if(!object.isNull()) { if(m_state.has(m_state.get_pods(), data.m_uid)) { std::ostringstream os; os << "ADDED message received for existing pod [" << data.m_uid << "], updating only."; g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); } k8s_pod_t& pod = m_state.get_component(m_state.get_pods(), data.m_name, data.m_uid, data.m_namespace); handle_labels(pod, object["metadata"], "labels"); m_state.update_pod(pod, object); } } else if(data.m_reason == COMPONENT_MODIFIED) { const Json::Value& object = root["object"]; if(!object.isNull()) { if(!m_state.has(m_state.get_pods(), data.m_uid)) { std::ostringstream os; os << "MODIFIED message received for non-existing pod [" << data.m_uid << "], giving up."; g_logger.log(os.str(), sinsp_logger::SEV_ERROR); return false; } k8s_pod_t& pod = m_state.get_component(m_state.get_pods(), data.m_name, data.m_uid, data.m_namespace); handle_labels(pod, object["metadata"], "labels"); m_state.update_pod(pod, object); } } else if(data.m_reason == COMPONENT_DELETED) { k8s_pod_t* pod = m_state.get_component(m_state.get_pods(), data.m_uid); if(pod) { if(!m_state.delete_component(m_state.get_pods(), data.m_uid)) { g_logger.log(std::string("Error deleting POD: ") + data.m_name, sinsp_logger::SEV_ERROR); return false; } } else { g_logger.log(std::string("POD not found: ") + data.m_name, sinsp_logger::SEV_WARNING); return false; } } else if(data.m_reason == COMPONENT_ERROR) { log_error(root, "POD"); } else { g_logger.log(std::string("Unsupported K8S POD event reason: ") + std::to_string(data.m_reason), sinsp_logger::SEV_ERROR); return false; } return true; } void k8s_dispatcher::handle_service(const Json::Value& root, const msg_data& data) { if(data.m_reason == COMPONENT_ADDED) { const Json::Value& object = root["object"]; if(!object.isNull()) { if(m_state.has(m_state.get_services(), data.m_uid)) { std::ostringstream os; os << "ADDED message received for existing service [" << data.m_uid << "], updating only."; g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); } k8s_service_t& service = m_state.get_component(m_state.get_services(), data.m_name, data.m_uid, data.m_namespace); handle_labels(service, object["metadata"], "labels"); handle_selectors(service, object["spec"]); k8s_service_handler::extract_services_data(object, service, m_state.get_pods()); } else { g_logger.log("K8s: object is null for service " + data.m_name + '[' + data.m_uid + ']', sinsp_logger::SEV_ERROR); } } else if(data.m_reason == COMPONENT_MODIFIED) { const Json::Value& object = root["object"]; if(!object.isNull()) { if(!m_state.has(m_state.get_services(), data.m_uid)) { std::ostringstream os; os << "MODIFIED message received for non-existing service [" << data.m_uid << "], giving up."; g_logger.log(os.str(), sinsp_logger::SEV_ERROR); return; } k8s_service_t& service = m_state.get_component(m_state.get_services(), data.m_name, data.m_uid, data.m_namespace); handle_labels(service, object["metadata"], "labels"); handle_selectors(service, object["spec"]); k8s_service_handler::extract_services_data(object, service, m_state.get_pods()); } else { g_logger.log("K8s: object is null for service " + data.m_name + '[' + data.m_uid + ']', sinsp_logger::SEV_ERROR); } } else if(data.m_reason == COMPONENT_DELETED) { if(!m_state.delete_component(m_state.get_services(), data.m_uid)) { g_logger.log(std::string("SERVICE not found: ") + data.m_name, sinsp_logger::SEV_ERROR); } } else if(data.m_reason == COMPONENT_ERROR) { log_error(root, "SERVICE"); } else { g_logger.log(std::string("Unsupported K8S SERVICE event reason: ") + std::to_string(data.m_reason), sinsp_logger::SEV_ERROR); } } void k8s_dispatcher::handle_deployment(const Json::Value& root, const msg_data& data) { if(data.m_reason == COMPONENT_ADDED) { const Json::Value& object = root["object"]; if(!object.isNull()) { if(m_state.has(m_state.get_deployments(), data.m_uid)) { std::ostringstream os; os << "ADDED message received for existing deployment [" << data.m_uid << "], updating only."; g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); } k8s_deployment_t& deployment = m_state.get_component(m_state.get_deployments(), data.m_name, data.m_uid, data.m_namespace); handle_labels(deployment, object["metadata"], "labels"); handle_selectors(deployment, object["spec"]); deployment.set_replicas(object); } else { g_logger.log("K8s: object is null for deployment "+ data.m_name + '[' + data.m_uid + ']', sinsp_logger::SEV_ERROR); } } else if(data.m_reason == COMPONENT_MODIFIED) { const Json::Value& object = root["object"]; if(!object.isNull()) { if(!m_state.has(m_state.get_deployments(), data.m_uid)) { std::ostringstream os; os << "MODIFIED message received for non-existing deployment [" << data.m_uid << "], giving up."; g_logger.log(os.str(), sinsp_logger::SEV_ERROR); return; } k8s_deployment_t& deployment = m_state.get_component(m_state.get_deployments(), data.m_name, data.m_uid, data.m_namespace); handle_labels(deployment, object["metadata"], "labels"); handle_selectors(deployment, object["spec"]); deployment.set_replicas(object); } else { g_logger.log("K8s: object is null for deployment " + data.m_name + '[' + data.m_uid + ']', sinsp_logger::SEV_ERROR); } } else if(data.m_reason == COMPONENT_DELETED) { if(!m_state.delete_component(m_state.get_deployments(), data.m_uid)) { g_logger.log(std::string("DEPLOYMENT not found: ") + data.m_name, sinsp_logger::SEV_ERROR); } } else if(data.m_reason == COMPONENT_ERROR) { log_error(root, "DEPLOYMENT"); } else { g_logger.log(std::string("Unsupported K8S DEPLOYMENT event reason: ") + std::to_string(data.m_reason), sinsp_logger::SEV_ERROR); } } void k8s_dispatcher::handle_daemonset(const Json::Value& root, const msg_data& data) { if(data.m_reason == COMPONENT_ADDED) { const Json::Value& object = root["object"]; if(!object.isNull()) { if(m_state.has(m_state.get_daemonsets(), data.m_uid)) { std::ostringstream os; os << "ADDED message received for existing daemonset [" << data.m_uid << "], updating only."; g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); } k8s_daemonset_t& daemonset = m_state.get_component(m_state.get_daemonsets(), data.m_name, data.m_uid, data.m_namespace); handle_labels(daemonset, object["metadata"], "labels"); handle_selectors(daemonset, object["spec"]); daemonset.set_scheduled(object); } } else if(data.m_reason == COMPONENT_MODIFIED) { const Json::Value& object = root["object"]; if(!object.isNull()) { if(!m_state.has(m_state.get_daemonsets(), data.m_uid)) { std::ostringstream os; os << "MODIFIED message received for non-existing daemonset [" << data.m_uid << "], giving up."; g_logger.log(os.str(), sinsp_logger::SEV_ERROR); return; } k8s_daemonset_t& daemonset = m_state.get_component(m_state.get_daemonsets(), data.m_name, data.m_uid, data.m_namespace); handle_labels(daemonset, object["metadata"], "labels"); handle_selectors(daemonset, object["spec"]); daemonset.set_scheduled(object); } } else if(data.m_reason == COMPONENT_DELETED) { if(!m_state.delete_component(m_state.get_daemonsets(), data.m_uid)) { g_logger.log(std::string("DAEMONSET not found: ") + data.m_name, sinsp_logger::SEV_ERROR); } } else if(data.m_reason == COMPONENT_ERROR) { log_error(root, "DAEMONSET"); } else { g_logger.log(std::string("Unsupported K8S DAEMONSET event reason: ") + std::to_string(data.m_reason), sinsp_logger::SEV_ERROR); } } void k8s_dispatcher::handle_event(const Json::Value& root, const msg_data& data) { if(m_event_filter) { const Json::Value& object = root["object"]; if(!object.isNull()) { g_logger.log("K8s EVENT: object found.", sinsp_logger::SEV_TRACE); const Json::Value& involved_object = object["involvedObject"]; if(!involved_object.isNull()) { bool is_aggregate = (get_json_string(object , "message").find("events with common reason combined") != std::string::npos); time_t last_ts = get_epoch_utc_seconds(get_json_string(object , "lastTimestamp")); time_t now_ts = get_epoch_utc_seconds_now(); g_logger.log("K8s EVENT: lastTimestamp=" + std::to_string(last_ts) + ", now_ts=" + std::to_string(now_ts), sinsp_logger::SEV_TRACE); if(((last_ts > 0) && (now_ts > 0)) && // we got good timestamps !is_aggregate && // not an aggregated cached event ((now_ts - last_ts) < 10)) // event not older than 10 seconds { const Json::Value& kind = involved_object["kind"]; const Json::Value& event_reason = object["reason"]; g_logger.log("K8s EVENT: involved object and event reason found:" + kind.asString() + '/' + event_reason.asString(), sinsp_logger::SEV_TRACE); if(!kind.isNull() && kind.isConvertibleTo(Json::stringValue) && !event_reason.isNull() && event_reason.isConvertibleTo(Json::stringValue)) { bool is_allowed = m_event_filter->allows_all(); std::string type = kind.asString(); if(!is_allowed && !type.empty()) { std::string reason = event_reason.asString(); is_allowed = m_event_filter->allows_all(type); if(!is_allowed && !reason.empty()) { is_allowed = m_event_filter->has(type, reason); } } if(is_allowed) { g_logger.log("K8s EVENT: adding event.", sinsp_logger::SEV_TRACE); k8s_event_t& evt = m_state.add_component(m_state.get_events(), data.m_name, data.m_uid, data.m_namespace); m_state.update_event(evt, object); } else { g_logger.log("K8s EVENT: filter does not allow {\"" + type + "\", \"{" + event_reason.asString() + "\"} }", sinsp_logger::SEV_TRACE); g_logger.log(m_event_filter->to_string(), sinsp_logger::SEV_TRACE); } } else { g_logger.log("K8s EVENT: event type or involvedObject kind not found.", sinsp_logger::SEV_ERROR); g_logger.log(Json::FastWriter().write(root), sinsp_logger::SEV_TRACE); } } else { g_logger.log("K8s EVENT: old event, ignoring: " ", lastTimestamp=" + std::to_string(last_ts) + ", now_ts=" + std::to_string(now_ts), sinsp_logger::SEV_DEBUG); } } else { g_logger.log("K8s EVENT: involvedObject not found.", sinsp_logger::SEV_ERROR); g_logger.log(Json::FastWriter().write(root), sinsp_logger::SEV_TRACE); } } else { g_logger.log("K8s EVENT: object not found.", sinsp_logger::SEV_ERROR); g_logger.log(Json::FastWriter().write(root), sinsp_logger::SEV_TRACE); } } else { g_logger.log("K8s EVENT: filter NOT found.", sinsp_logger::SEV_DEBUG); } } void k8s_dispatcher::extract_data(Json::Value& root, bool enqueue) { std::ostringstream os; msg_data data = get_msg_data(root); if(data.is_valid()) { std::ostringstream os; os << '[' << to_reason_desc(data.m_reason) << ','; switch (m_type) { case k8s_component::K8S_NODES: os << "NODE,"; handle_node(root, data); break; case k8s_component::K8S_NAMESPACES: os << "NAMESPACE,"; handle_namespace(root, data); break; case k8s_component::K8S_PODS: os << "POD,"; if(handle_pod(root, data)) { break; } else { return; } case k8s_component::K8S_REPLICATIONCONTROLLERS: os << "REPLICATION_CONTROLLER,"; handle_rc(root, data, m_state.get_rcs(), "replication controller"); break; case k8s_component::K8S_REPLICASETS: os << "REPLICA_SET,"; handle_rc(root, data, m_state.get_rss(), "replica set"); break; case k8s_component::K8S_SERVICES: os << "SERVICE,"; handle_service(root, data); break; case k8s_component::K8S_DAEMONSETS: os << "DAEMON_SET,"; handle_daemonset(root, data); break; case k8s_component::K8S_DEPLOYMENTS: os << "DEPLOYMENT,"; handle_deployment(root, data); break; case k8s_component::K8S_EVENTS: os << "EVENT,"; if(m_event_filter) { handle_event(root, data); } break; default: { std::ostringstream eos; eos << "Unknown component: " << static_cast(m_type); throw sinsp_exception(os.str()); } } os << data.m_name << ',' << data.m_uid << ',' << data.m_namespace << ']'; g_logger.log(os.str(), sinsp_logger::SEV_INFO); //g_logger.log(root.toStyledString(), sinsp_logger::SEV_DEBUG); m_state.update_cache(m_type); #ifdef HAS_CAPTURE if(enqueue) { m_state.enqueue_capture_event(root); } #endif } } void k8s_dispatcher::extract_data(const std::string& json, bool enqueue) { Json::Value root; Json::Reader reader; if(reader.parse(json, root, false)) { extract_data(root, enqueue); } else { g_logger.log("Bad JSON message received :[" + json + ']', sinsp_logger::SEV_ERROR); } } void k8s_dispatcher::dispatch() { for (list::iterator it = m_messages.begin(); it != m_messages.end();) { if(is_ready(*it)) { extract_data(*it, true); it = m_messages.erase(it); } else { ++it; } } } std::string k8s_dispatcher::to_reason_desc(msg_reason reason) { switch (reason) { case COMPONENT_ADDED: return "ADDED"; case COMPONENT_MODIFIED: return "MODIFIED"; case COMPONENT_DELETED: return "DELETED"; case COMPONENT_ERROR: return "ERROR"; case COMPONENT_UNKNOWN: return "UNKNOWN"; default: return ""; } } k8s_dispatcher::msg_reason k8s_dispatcher::to_reason(const std::string& desc) { if(desc == "ADDED") { return COMPONENT_ADDED; } else if(desc == "MODIFIED") { return COMPONENT_MODIFIED; } else if(desc == "DELETED") { return COMPONENT_DELETED; } else if(desc == "ERROR") { return COMPONENT_ERROR; } else if(desc == "UNKNOWN") { return COMPONENT_UNKNOWN; } throw sinsp_exception(desc); } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/k8s_dispatcher.h000066400000000000000000000142731352731327100220210ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_dispatcher.h // // kubernetes REST API notification abstraction // #pragma once #include "k8s.h" #include "k8s_component.h" #include "k8s_state.h" #include "k8s_event_data.h" #include "json/json.h" #include #include class k8s_dispatcher { public: typedef user_event_filter_t::ptr_t filter_ptr_t; enum msg_reason { COMPONENT_ADDED, COMPONENT_MODIFIED, COMPONENT_DELETED, COMPONENT_ERROR, COMPONENT_UNKNOWN // only to mark bad event messages }; struct msg_data { msg_reason m_reason = COMPONENT_UNKNOWN; std::string m_name; std::string m_uid; std::string m_namespace; bool is_valid() const { return m_reason != COMPONENT_UNKNOWN; } }; k8s_dispatcher() = delete; k8s_dispatcher(k8s_component::type t, k8s_state_t& state, filter_ptr_t event_filter = nullptr); void enqueue(k8s_event_data&& data); void extract_data(const std::string& json, bool enqueue = false); void extract_data(Json::Value& root, bool enqueue = false); // clears the content of labels and fills it with new values, if any template static void handle_labels(T& component, const Json::Value& metadata, const std::string& name) { if(!metadata.isNull()) { k8s_pair_list entries = k8s_component::extract_object(metadata, name); component.set_labels(std::move(entries)); } else { g_logger.log("Null metadata object received", sinsp_logger::SEV_ERROR); } } // clears the content of selectors and fills it with new values, if any; // the selector location depth in JSON tree is detected and handled accordingly template static void handle_selectors(T& component, const Json::Value& spec) { if(!spec.isNull()) { const Json::Value& selector = spec["selector"]; if(!selector.isNull()) { const Json::Value& match_labels = selector["matchLabels"]; k8s_pair_list selectors = match_labels.isNull() ? k8s_component::extract_object(spec, "selector") : k8s_component::extract_object(selector, "matchLabels"); component.set_selectors(std::move(selectors)); } else { g_logger.log("K8s: Null selector object.", sinsp_logger::SEV_ERROR); } } else { g_logger.log("K8s: Null spec object.", sinsp_logger::SEV_ERROR); } } private: const std::string& next_msg(); msg_data get_msg_data(Json::Value& root); bool is_valid(const std::string& msg); bool is_ready(const std::string& msg); void remove(); void dispatch(); void handle_node(const Json::Value& root, const msg_data& data); void handle_namespace(const Json::Value& root, const msg_data& data); bool handle_pod(const Json::Value& root, const msg_data& data); void handle_service(const Json::Value& root, const msg_data& data); void handle_deployment(const Json::Value& root, const msg_data& data); void handle_daemonset(const Json::Value& root, const msg_data& data); void handle_event(const Json::Value& root, const msg_data& data); // handler for replication controllers and replica sets template void handle_rc(const Json::Value& root, const msg_data& data, T& cont, const std::string& comp_name) { typedef typename T::value_type comp_t; if(data.m_reason == COMPONENT_ADDED) { if(m_state.has(cont, data.m_uid)) { std::ostringstream os; os << "ADDED message received for existing " << comp_name << '[' << data.m_uid << "], updating only."; g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); } comp_t& rc = m_state.get_component(cont, data.m_name, data.m_uid, data.m_namespace); const Json::Value& object = root["object"]; if(!object.isNull()) { handle_labels(rc, object["metadata"], "labels"); const Json::Value& spec = object["spec"]; handle_selectors(rc, spec); rc.set_replicas(object); } else { g_logger.log("K8s: object is null for " + comp_name + ' ' + data.m_name + '[' + data.m_uid + ']', sinsp_logger::SEV_ERROR); } } else if(data.m_reason == COMPONENT_MODIFIED) { if(!m_state.has(cont, data.m_uid)) { std::ostringstream os; os << "MODIFIED message received for non-existing " << comp_name << '[' << data.m_uid << "], giving up."; g_logger.log(os.str(), sinsp_logger::SEV_ERROR); return; } comp_t& rc = m_state.get_component(cont, data.m_name, data.m_uid, data.m_namespace); const Json::Value& object = root["object"]; if(!object.isNull()) { handle_labels(rc, object["metadata"], "labels"); handle_selectors(rc, object["spec"]); rc.set_replicas(object); } else { g_logger.log("K8s: object is null for " + comp_name + ' ' + data.m_name + '[' + data.m_uid + ']', sinsp_logger::SEV_ERROR); } } else if(data.m_reason == COMPONENT_DELETED) { if(!m_state.delete_component(cont, data.m_uid)) { g_logger.log("K8s: " + comp_name + " not found: " + data.m_name, sinsp_logger::SEV_ERROR); } } else if(data.m_reason == COMPONENT_ERROR) { log_error(root, comp_name); } else { g_logger.log(std::string("Unsupported K8S " + comp_name + " event reason: ") + std::to_string(data.m_reason), sinsp_logger::SEV_ERROR); } } void log_error(const Json::Value& root, const std::string& comp); static std::string to_reason_desc(msg_reason reason); static msg_reason to_reason(const std::string& desc); typedef std::deque list; k8s_component::type m_type; list m_messages; k8s_state_t& m_state; filter_ptr_t m_event_filter; std::string m_machine_id; }; inline const std::string& k8s_dispatcher::next_msg() { return m_messages.front(); } inline void k8s_dispatcher::remove() { m_messages.pop_front(); }sysdig-0.26.4/userspace/libsinsp/k8s_event_data.cpp000066400000000000000000000023161352731327100223330ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_net.cpp // #include "k8s_event_data.h" k8s_event_data::k8s_event_data(k8s_component::type component, const char* data, int len): m_component(component), m_data(data, len) { } k8s_event_data::k8s_event_data(const k8s_event_data& other): m_component(other.m_component), m_data(other.m_data) { } k8s_event_data::k8s_event_data(k8s_event_data&& other): m_component(std::move(other.m_component)), m_data(std::move(other.m_data)) { } k8s_event_data& k8s_event_data::operator=(k8s_event_data&& other) { if(this != &other) { m_component = other.m_component; m_data = other.m_data; } return *this; } sysdig-0.26.4/userspace/libsinsp/k8s_event_data.h000066400000000000000000000024511352731327100220000ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_event_data.h // // connects and gets the data from k8s_net REST API interface // #pragma once #include "k8s_component.h" class k8s_event_data { public: k8s_event_data() = delete; k8s_event_data(k8s_component::type component, const char* data, int len); k8s_event_data(const k8s_event_data& other); k8s_event_data(k8s_event_data&& other); k8s_event_data& operator=(k8s_event_data&& other); k8s_component::type component() const; std::string data() const; private: k8s_component::type m_component; std::string m_data; }; inline k8s_component::type k8s_event_data::component() const { return m_component; } inline std::string k8s_event_data::data() const { return m_data; }sysdig-0.26.4/userspace/libsinsp/k8s_event_handler.cpp000066400000000000000000000224501352731327100230400ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_event_handler.cpp // #ifndef CYGWING_AGENT #include "k8s_event_handler.h" #include "sinsp.h" #include "sinsp_int.h" // filters normalize state and event JSONs, so they can be processed generically: // event is turned into a single-entry array, state is turned into an array of ADDED events std::string k8s_event_handler::EVENT_FILTER = "{" " type: .type," " apiVersion: .object.apiVersion," " kind: .object.kind," " items:" " [" " .object |" " {" " namespace: .metadata.namespace," " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " lastTimestamp: .lastTimestamp," " reason: .reason," " message: .message," " involvedObject: .involvedObject" " }" " ]" "}"; std::string k8s_event_handler::STATE_FILTER = "{" " type: \"ADDED\"," " apiVersion: .apiVersion," " kind: \"Event\"," " items:" " [" " .items[] |" " {" " namespace: .metadata.namespace," " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " lastTimestamp: .lastTimestamp," " reason: .reason," " message: .message," " involvedObject: .involvedObject" " }" " ]" "}"; k8s_event_handler::k8s_event_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector ,std::string url ,const std::string& http_version ,ssl_ptr_t ssl ,bt_ptr_t bt ,bool connect ,bool blocking_socket #endif // HAS_CAPTURE ,filter_ptr_t event_filter): k8s_handler("k8s_event_handler", true, #ifdef HAS_CAPTURE url, "/api/v1/events", STATE_FILTER, EVENT_FILTER, "", collector, http_version, 1000L, ssl, bt, true, connect, dependency_handler, blocking_socket, #endif // HAS_CAPTURE ~0, &state), m_event_filter(event_filter) { } k8s_event_handler::~k8s_event_handler() { } bool k8s_event_handler::handle_component(const Json::Value& json, const msg_data* data) { if(m_event_filter) { if(m_state) { if(data) { if((data->m_reason == k8s_component::COMPONENT_ADDED) || (data->m_reason == k8s_component::COMPONENT_MODIFIED)) { g_logger.log("K8s EVENT: handling event.", sinsp_logger::SEV_TRACE); const Json::Value& involved_object = json["involvedObject"]; if(!involved_object.isNull()) { bool is_aggregate = (get_json_string(json , "message").find("events with common reason combined") != std::string::npos); time_t last_ts = get_epoch_utc_seconds(get_json_string(json , "lastTimestamp")); time_t now_ts = get_epoch_utc_seconds_now(); g_logger.log("K8s EVENT: lastTimestamp=" + std::to_string(last_ts) + ", now_ts=" + std::to_string(now_ts), sinsp_logger::SEV_TRACE); if(((last_ts > 0) && (now_ts > 0)) && // we got good timestamps !is_aggregate && // not an aggregated cached event ((now_ts - last_ts) < 10)) // event not older than 10 seconds { const Json::Value& kind = involved_object["kind"]; const Json::Value& event_reason = json["reason"]; g_logger.log("K8s EVENT: involved object and event reason found:" + kind.asString() + '/' + event_reason.asString(), sinsp_logger::SEV_TRACE); if(!kind.isNull() && kind.isConvertibleTo(Json::stringValue) && !event_reason.isNull() && event_reason.isConvertibleTo(Json::stringValue)) { bool is_allowed = m_event_filter->allows_all(); std::string type = kind.asString(); if(!is_allowed && !type.empty()) { std::string reason = event_reason.asString(); is_allowed = m_event_filter->allows_all(type); if(!is_allowed && !reason.empty()) { is_allowed = m_event_filter->has(type, reason); } } if(is_allowed) { k8s_events& evts = m_state->get_events(); if(evts.size() < sinsp_user_event::max_events_per_cycle()) { k8s_event_t& evt = m_state->add_component(evts, data->m_name, data->m_uid, data->m_namespace); m_state->update_event(evt, json); m_event_limit_exceeded = false; if(g_logger.get_severity() >= sinsp_logger::SEV_DEBUG) { g_logger.log("K8s EVENT: added event [" + data->m_name + "]. " "Queued events count=" + std::to_string(evts.size()), sinsp_logger::SEV_DEBUG); } } else if(!m_event_limit_exceeded) // only get in here once per cycle, to send event overflow warning { sinsp_user_event::emit_event_overflow("Kubernetes", get_machine_id()); m_event_limit_exceeded = true; return false; } else // event limit exceeded and overflow logged, nothing to do { return false; } } else // event not allowed by filter, ignore { if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log("K8s EVENT: filter does not allow {\"" + type + "\", \"{" + event_reason.asString() + "\"} }", sinsp_logger::SEV_TRACE); g_logger.log(m_event_filter->to_string(), sinsp_logger::SEV_TRACE); } m_event_ignored = true; return false; } } else { g_logger.log("K8s EVENT: event type or involvedObject kind not found.", sinsp_logger::SEV_ERROR); if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log(Json::FastWriter().write(json), sinsp_logger::SEV_TRACE); } return false; } } else // old event, ignore { g_logger.log("K8s EVENT: old event, ignoring: " ", lastTimestamp=" + std::to_string(last_ts) + ", now_ts=" + std::to_string(now_ts), sinsp_logger::SEV_DEBUG); m_event_ignored = true; return false; } } else { g_logger.log("K8s EVENT: involvedObject not found.", sinsp_logger::SEV_ERROR); g_logger.log(Json::FastWriter().write(json), sinsp_logger::SEV_TRACE); return false; } } else // not ADDED or MODIFIED event, ignore { m_event_ignored = true; return false; } } else { g_logger.log("K8s EVENT: msg data is null.", sinsp_logger::SEV_ERROR); g_logger.log(Json::FastWriter().write(json), sinsp_logger::SEV_TRACE); return false; } } else { g_logger.log("K8s EVENT: state is null.", sinsp_logger::SEV_ERROR); return false; } } else { g_logger.log("K8s EVENT: no filter, K8s events disabled.", sinsp_logger::SEV_TRACE); return false; } return true; } void k8s_event_handler::handle_json(Json::Value&& root) { /*if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log(json_as_string(root), sinsp_logger::SEV_TRACE); }*/ if(!m_state) { throw sinsp_exception("k8s_handler (" + get_id() + "), state is null for " + get_url() + ")."); } const Json::Value& type = root["type"]; if(!type.isNull()) { if(type.isConvertibleTo(Json::stringValue)) { const Json::Value& kind = root["kind"]; if(!kind.isNull()) { if(kind.isConvertibleTo(Json::stringValue)) { std::string t = type.asString(); std::string k = kind.asString(); for(const Json::Value& item : root["items"]) { msg_data data = get_msg_data(t, k, item); std::string reason_type = data.get_reason_desc(); if(data.m_reason != k8s_component::COMPONENT_ADDED && data.m_reason != k8s_component::COMPONENT_MODIFIED && data.m_reason != k8s_component::COMPONENT_DELETED && data.m_reason != k8s_component::COMPONENT_NONEXISTENT && data.m_reason != k8s_component::COMPONENT_ERROR) { g_logger.log(std::string("Unsupported K8S " + name() + " event reason: ") + std::to_string(data.m_reason), sinsp_logger::SEV_ERROR); continue; } /*if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log("K8s handling event:\n" + json_as_string(item), sinsp_logger::SEV_TRACE); }*/ if(handle_component(item, &data)) { std::ostringstream os; os << "K8s [" + reason_type + ", " << data.m_kind << ", " << data.m_name << ", " << data.m_uid << "]"; g_logger.log(os.str(), sinsp_logger::SEV_INFO); } else if(!m_event_ignored) { g_logger.log("K8s: error occurred while handling " + reason_type + " event for " + data.m_kind + ' ' + data.m_name + " [" + data.m_uid + ']', sinsp_logger::SEV_ERROR); } m_event_ignored = false; } // end for items } } } else { g_logger.log(std::string("K8S event type is not string."), sinsp_logger::SEV_ERROR); } } else { g_logger.log(std::string("K8S event type is null."), sinsp_logger::SEV_ERROR); } } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/k8s_event_handler.h000066400000000000000000000030211352731327100224760ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_event_handler.h // #pragma once #include "json/json.h" #include "sinsp_auth.h" #include "k8s_handler.h" #include "k8s_event_data.h" class sinsp; class k8s_event_handler : public k8s_handler { public: typedef user_event_filter_t::ptr_t filter_ptr_t; k8s_event_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector = nullptr ,std::string url = "" ,const std::string& http_version = "1.1" ,ssl_ptr_t ssl = 0 ,bt_ptr_t bt = 0 ,bool connect = true ,bool blocking_socket = false #endif // HAS_CAPTURE ,filter_ptr_t event_filter = 0); ~k8s_event_handler(); private: static std::string EVENT_FILTER; static std::string STATE_FILTER; bool handle_component(const Json::Value& json, const msg_data* data = 0); void handle_json(Json::Value&& root); filter_ptr_t m_event_filter; bool m_event_ignored = false; bool m_event_limit_exceeded = false; }; sysdig-0.26.4/userspace/libsinsp/k8s_handler.cpp000066400000000000000000000541111352731327100216360ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_handler.cpp // #ifndef CYGWING_AGENT #include "k8s_handler.h" #include "sinsp.h" #include "sinsp_int.h" // to match regular K8s API message format, // error is wrapped into a single-entry array std::string k8s_handler::ERROR_FILTER = "{" " type: \"ERROR\"," " apiVersion: .apiVersion," " kind: .kind," " items:" " [" " . |" " {" " metadata: .metadata," " status: .status," " message: .message," " reason: .reason," " details: .details," " code: .code" " }" " ]" "}"; k8s_handler::k8s_handler(const std::string& id, bool is_captured, #ifdef HAS_CAPTURE std::string url, const std::string& path, const std::string& state_filter, const std::string& event_filter, const std::string& null_filter, collector_ptr_t collector, const std::string& http_version, int timeout_ms, ssl_ptr_t ssl, bt_ptr_t bt, bool watch, bool connect, ptr_t dependency_handler, bool blocking_socket, #endif // HAS_CAPTURE unsigned max_messages, k8s_state_t* state): m_state(state), m_id(id + "_state"), #ifdef HAS_CAPTURE m_collector(collector), m_path(path + ((path.find('?') == std::string::npos) ? "?pretty=false" : "&pretty=false")), m_state_filter(state_filter), m_event_filter(event_filter), m_null_filter(null_filter), m_filter(&m_state_filter), m_timeout_ms(timeout_ms), m_url(url), m_http_version(http_version), m_ssl(ssl), m_bt(bt), m_watch(watch), m_connect(connect), m_dependency_handler(dependency_handler), m_blocking_socket(blocking_socket), #endif // HAS_CAPTURE m_max_messages(max_messages), m_is_captured(is_captured) { #ifdef HAS_CAPTURE g_logger.log("Creating K8s " + name() + " (" + m_id + ") " "handler object for [" + uri(m_url).to_string(false) + m_path + ']', sinsp_logger::SEV_DEBUG); if(m_connect) { g_logger.log(std::string("K8s (" + m_id + ") creating handler for " + uri(m_url).to_string(false) + m_path), sinsp_logger::SEV_DEBUG); m_handler = std::make_shared(*this, m_id, m_url, m_path, m_http_version, m_timeout_ms, m_ssl, m_bt, !m_blocking_socket, m_blocking_socket); m_handler->set_json_callback(&k8s_handler::set_event_json); // filter order is important; there are four kinds of filters: // 1.a state filter (filters init state JSONs) // 1.b event filter (filters watch JSONs) // 2. null filter (filters init state JSONs when there are no K8s entities) // 3. error filter (filters errors) // and they must come in the following order: // 1.a OR 1.b, then 2. (for state only), then 3. (always) // mixing the order will produce erroneous results because // null and error filters will successfully parse regular messages // and prevent proper processing m_handler->add_json_filter(*m_filter); if(!m_null_filter.empty()) { m_handler->add_json_filter(m_null_filter); } m_handler->add_json_filter(ERROR_FILTER); m_handler->close_on_chunked_end(false); this->connect(); } #endif // HAS_CAPTURE } k8s_handler::~k8s_handler() { } void k8s_handler::make_http() { #ifdef HAS_CAPTURE if(m_connect && m_collector) { if(!m_handler) { g_logger.log("K8s (" + m_id + ") creating handler for " + uri(m_url).to_string(false) + m_path, sinsp_logger::SEV_INFO); m_handler = std::make_shared(*this, m_id, m_url, m_path, m_http_version, m_timeout_ms, m_ssl, m_bt, true, m_blocking_socket); m_handler->set_json_callback(&k8s_handler::set_event_json); } else if(m_collector->has(m_handler)) { m_collector->remove(m_handler); } // adjust filters for event handler // (see comment in ctor for explanation) // - null filter is only needed for init state m_handler->remove_json_filter(m_null_filter); // - state filter will be replaced with the event filter below m_handler->remove_json_filter(m_state_filter); m_filter = &m_event_filter; // - error filter should be there, but just in case let's double-check if(!m_handler->has_json_filter(ERROR_FILTER)) { g_logger.log("K8s (" + m_id + ") error filter was not present in state handler, adding it for events.", sinsp_logger::SEV_WARNING); m_handler->add_json_filter(ERROR_FILTER); } // - good event filter must always be before error event filter m_handler->add_json_filter(*m_filter, ERROR_FILTER); // end filter adjustment m_handler->set_path(m_path); m_handler->set_id(m_id); m_collector->set_steady_state(true); m_watching = true; m_blocking_socket = false; m_handler->close_on_chunked_end(false); m_req_sent = false; m_resp_recvd = false; connect(); m_handler->set_socket_option(SOCK_NONBLOCK); } #endif // HAS_CAPTURE } void k8s_handler::check_enabled() { #ifdef HAS_CAPTURE if(!m_handler->is_enabled()) { g_logger.log("k8s_handler (" + m_id + ") check_enabled() enabling socket in collector", sinsp_logger::SEV_TRACE); m_handler->enable(); } else { g_logger.log("k8s_handler (" + m_id + ") check_enabled() socket in collector is enabled, " "checking collector status.", sinsp_logger::SEV_TRACE); check_collector_status(); } #endif // HAS_CAPTURE } bool k8s_handler::connect() { #ifdef HAS_CAPTURE if(m_collector && m_handler) { if(!m_collector->has(m_handler)) { g_logger.log(std::string("k8s_handler (" + m_id + ") k8s_handler::connect() adding handler to collector"), sinsp_logger::SEV_TRACE); m_collector->add(m_handler); return false; } if(m_handler->is_connecting()) { g_logger.log(std::string("k8s_handler (" + m_id + "), k8s_handler::connect() connecting to " + m_handler->get_url().to_string(false)), sinsp_logger::SEV_TRACE); return false; } if(m_handler->is_connected()) { g_logger.log("k8s_handler (" + m_id + ") k8s_handler::connect() socket is connected.", sinsp_logger::SEV_TRACE); check_enabled(); return true; } } else if (m_collector && !m_url.empty()) { g_logger.log(std::string("k8s_handler (" + m_id + ") k8s_handler::connect(), http is null, (re)creating ... "), sinsp_logger::SEV_WARNING); make_http(); } #endif // HAS_CAPTURE return false; } void k8s_handler::send_data_request() { #ifdef HAS_CAPTURE if(m_handler) { if(!m_req_sent) { if(m_handler->is_connected()) { g_logger.log("k8s_handler (" + m_id + ") sending request to " + m_handler->get_url().to_string(false) + m_path, sinsp_logger::SEV_DEBUG); m_handler->send_request(); m_req_sent = true; } else if(m_handler->is_connecting()) { g_logger.log("k8s_handler (" + m_id + ") is connecting to " + m_handler->get_url().to_string(false), sinsp_logger::SEV_DEBUG); } } } else { throw sinsp_exception("k8s_handler (" + m_id + ") HTTP client (" + uri(m_url).to_string(false) + ") is null."); } #endif // HAS_CAPTURE } void k8s_handler::receive_response() { #ifdef HAS_CAPTURE if(m_handler) { if(m_req_sent) { if(!m_watching) { if(m_handler->get_all_data()) { m_data_received = true; } else { throw sinsp_exception("K8s k8s_handler::receive_response(): no data received."); } } else { throw sinsp_exception("K8s k8s_handler::receive_response(): invalid call (in watch mode)."); } } else { throw sinsp_exception("K8s k8s_handler::receive_response(): invalid call (request not sent)."); } } else { throw sinsp_exception("K8s k8s_handler::receive_response(): handler is null."); } #endif // HAS_CAPTURE } bool k8s_handler::is_alive() const { #ifdef HAS_CAPTURE if(m_handler && !m_handler->is_connecting() && !m_handler->is_connected()) { g_logger.log("k8s_handler (" + m_id + ") connection (" + m_handler->get_url().to_string(false) + ") loss.", sinsp_logger::SEV_WARNING); return false; } #endif // HAS_CAPTURE return true; } void k8s_handler::check_collector_status() { #ifdef HAS_CAPTURE if(m_collector) { if(!m_collector->has(m_handler)) { m_handler.reset(); make_http(); } } else { throw sinsp_exception("k8s_handler (" + m_id + ") collector is null."); } #endif // HAS_CAPTURE } void k8s_handler::check_state() { #ifdef HAS_CAPTURE if(m_collector && m_handler) { if(m_resp_recvd && m_watch && !m_watching) { g_logger.log("k8s_handler (" + m_id + ") switching to watch connection for " + uri(m_url).to_string(false) + m_path, sinsp_logger::SEV_DEBUG); std::string::size_type pos = m_id.find("_state"); if(pos != std::string::npos) { m_id = m_id.substr(0, pos).append("_event"); } pos = m_path.find("/watch"); if(pos == std::string::npos) { pos = m_path.rfind('/'); if(pos != std::string::npos) { m_path.insert(pos, "/watch"); } else { throw sinsp_exception("k8s_handler (" + m_id + "), invalid URL path: " + m_path); } } m_handler->set_socket_option(SOCK_NONBLOCK); make_http(); } if(m_watching && m_id.find("_state") == std::string::npos && m_handler->wants_send()) { m_req_sent = false; m_resp_recvd = false; } } #endif // HAS_CAPTURE } bool k8s_handler::connection_error() const { #ifdef HAS_CAPTURE if(m_handler) { return m_handler->connection_error(); } #endif // HAS_CAPTURE return false; } void k8s_handler::collect_data() { #ifdef HAS_CAPTURE if(m_collector && m_handler) { process_events(); // there may be leftovers from state connection closed by collector check_state(); // switch to events, if needed g_logger.log("k8s_handler (" + m_id + ")::collect_data(), checking connection to " + uri(m_url).to_string(false), sinsp_logger::SEV_DEBUG); if(m_handler->is_connecting()) { g_logger.log("k8s_handler (" + m_id + ")::collect_data(), connecting to " + uri(m_url).to_string(false), sinsp_logger::SEV_DEBUG); return; } else if(m_handler->is_connected()) { if(!m_connect_logged) { g_logger.log("k8s_handler (" + m_id + ")::collect_data(), connected to " + uri(m_url).to_string(false) + m_path, sinsp_logger::SEV_DEBUG); m_connect_logged = true; } check_enabled(); if(!m_req_sent) { g_logger.log("k8s_handler (" + m_id + ")::collect_data() [" + uri(m_url).to_string(false) + "], requesting data " "from " + m_path + "... m_blocking_socket=" + std::to_string(m_blocking_socket) + ", m_watching=" + std::to_string(m_watching), sinsp_logger::SEV_DEBUG); send_data_request(); if(m_blocking_socket && !m_watching) { receive_response(); process_events(); return; } } if(m_collector->subscription_count()) { g_logger.log("k8s_handler (" + m_id + ")::collect_data() [" + uri(m_url).to_string(false) + "], getting data " "from " + m_path + "...", sinsp_logger::SEV_DEBUG); m_collector->get_data(); g_logger.log("k8s_handler (" + m_id + ")::collect_data(), " + std::to_string(m_events.size()) + " events from " + uri(m_url).to_string(false) + m_path, sinsp_logger::SEV_DEBUG); if(m_events.size()) { g_logger.log("k8s_handler (" + m_id + ")::collect_data(), data from " + uri(m_url).to_string(false) + m_path + ", event count=" + std::to_string(m_events.size()), sinsp_logger::SEV_DEBUG); process_events(); check_state(); } else { g_logger.log("k8s_handler (" + m_id + ") collect_data(), no data from " + uri(m_url).to_string(false) + m_path, sinsp_logger::SEV_DEBUG); } } else { g_logger.log("k8s_handler (" + m_id + ") collect_data(), no subscriptions to " + uri(m_url).to_string(false) + m_path, sinsp_logger::SEV_DEBUG); } return; } else { connect(); } m_req_sent = false; } else { g_logger.log("k8s_handler (" + m_id + "), http interface not (yet?) created for " + uri(m_url).to_string(false) + ").", sinsp_logger::SEV_TRACE); } #endif // HAS_CAPTURE } k8s_handler::msg_data k8s_handler::get_msg_data(const std::string& type, const std::string& kind, const Json::Value& json) { msg_data data; if(!type.empty()) { if(type[0] == 'A') { data.m_reason = k8s_component::COMPONENT_ADDED; } else if(type[0] == 'M') { data.m_reason = k8s_component::COMPONENT_MODIFIED; } else if(type[0] == 'D') { data.m_reason = k8s_component::COMPONENT_DELETED; } else if(type[0] == 'N') { data.m_reason = k8s_component::COMPONENT_NONEXISTENT; } else if(type[0] == 'E') { data.m_reason = k8s_component::COMPONENT_ERROR; } } else { return data; } data.m_kind = kind; Json::Value name = json["name"]; if(!name.isNull()) { data.m_name = name.asString(); } Json::Value uid = json["uid"]; if(!uid.isNull()) { data.m_uid = uid.asString(); } Json::Value nspace = json["namespace"]; if(!nspace.isNull()) { data.m_namespace = nspace.asString(); } return data; } void k8s_handler::handle_json(Json::Value&& root) { /*if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log(json_as_string(root), sinsp_logger::SEV_TRACE); }*/ if(!m_state) { #ifdef HAS_CAPTURE throw sinsp_exception("k8s_handler (" + m_id + "), state is null for " + uri(m_url).to_string(false) + ")."); #else throw sinsp_exception("k8s_handler (" + m_id + "), state is null."); #endif // HAS_CAPTURE } const Json::Value& type = root["type"]; if(!type.isNull()) { if(type.isConvertibleTo(Json::stringValue)) { const Json::Value& kind = root["kind"]; if(!kind.isNull()) { if(kind.isConvertibleTo(Json::stringValue)) { std::string t = type.asString(); std::string k = kind.asString(); for(const Json::Value& item : root["items"]) { msg_data data = get_msg_data(t, k, item); /* uncomment to test proper error handling //if(name() == "replicasets") // vary name to verify (non)critical component error handling if(name() == "pods") { std::string j = "{" " \"metadata\": \"{}\"," " \"status\": \"Failure\"," " \"message\": \"the server could not find the requested resource\"," " \"reason\": \"NotFound\"," " \"details\": \"{}\"," " \"code\": 404" "}"; Json::Value i; Json::Reader().parse(j, i); data.m_reason = k8s_component::COMPONENT_ERROR; handle_error(data, i); continue; } */ std::string reason_type = data.get_reason_desc(); if(data.m_reason == k8s_component::COMPONENT_ADDED) { if(m_state->has(data.m_uid)) { std::ostringstream os; os << "K8s " + reason_type << " message received by " << m_id << #ifdef HAS_CAPTURE " [" << uri(m_url).to_string(false) << "]" #endif // HAS_CAPTURE "for existing " << data.m_kind << " [" << data.m_uid << "], updating only."; g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); } } else if(data.m_reason == k8s_component::COMPONENT_MODIFIED) { if(!m_state->has(data.m_uid)) { std::ostringstream os; os << "K8s " << reason_type << " message received by " << m_id << #ifdef HAS_CAPTURE " [" << uri(m_url).to_string(false) << "]" #endif // HAS_CAPTURE " for non-existing " << data.m_kind << " [" << data.m_uid << "], giving up."; g_logger.log(os.str(), sinsp_logger::SEV_WARNING); continue; } } else if(data.m_reason == k8s_component::COMPONENT_DELETED) { if(!m_state->has(data.m_uid)) { std::ostringstream os; os << "K8s " + reason_type + " message received by " << m_id << #ifdef HAS_CAPTURE " [" << uri(m_url).to_string(false) << "]" #endif // HAS_CAPTURE " for non-existing " << data.m_kind << " [" << data.m_uid << "], giving up."; g_logger.log(os.str(), sinsp_logger::SEV_WARNING); continue; } } else if(data.m_reason == k8s_component::COMPONENT_ERROR) { handle_error(data, item); continue; } else { if(data.m_reason == k8s_component::COMPONENT_NONEXISTENT) { g_logger.log(std::string("Non-existent K8S component (" + name() + "), reason: ") + std::to_string(data.m_reason), sinsp_logger::SEV_DEBUG); } else { g_logger.log(std::string("Unsupported K8S " + name() + " event reason: ") + std::to_string(data.m_reason), sinsp_logger::SEV_ERROR); } continue; } /*if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log("K8s handling item:\n" + json_as_string(item), sinsp_logger::SEV_TRACE); }*/ if(handle_component(item, &data)) { std::ostringstream os; os << "K8s [" + reason_type + ", " << data.m_kind << ", " << data.m_name << ", " << data.m_uid << "]"; g_logger.log(os.str(), sinsp_logger::SEV_INFO); m_state->update_cache(k8s_component::get_type(name())); } else { g_logger.log("K8s: error occurred while handling " + reason_type + " event for " + data.m_kind + ' ' + data.m_name + " [" + data.m_uid + ']', sinsp_logger::SEV_ERROR); } } // end for items } } } else { g_logger.log(std::string("K8S event type is not string."), sinsp_logger::SEV_ERROR); } } else { g_logger.log(std::string("K8S event type is null."), sinsp_logger::SEV_ERROR); } } #ifdef HAS_CAPTURE bool k8s_handler::is_ip_address(const std::string& addr) { struct sockaddr_in serv_addr = {0}; return inet_aton(addr.c_str(), &serv_addr.sin_addr); } k8s_handler::ip_addr_list_t k8s_handler::hostname_to_ip(const std::string& hostname) { ip_addr_list_t ip_addrs; struct addrinfo *servinfo = 0; struct addrinfo hints = {0}; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; if((getaddrinfo(hostname.c_str(), NULL, &hints, &servinfo)) != 0) { g_logger.log("Can't determine IP address for hostname: " + hostname, sinsp_logger::SEV_WARNING); return ip_addrs; } for(struct addrinfo* p = servinfo; p != NULL; p = p->ai_next) { struct sockaddr_in* h = (struct sockaddr_in*)p->ai_addr; ip_addrs.emplace(inet_ntoa(h->sin_addr)); } freeaddrinfo(servinfo); return ip_addrs; } #endif // HAS_CAPTURE bool k8s_handler::dependency_ready() const { #ifdef HAS_CAPTURE g_logger.log("k8s_handler (" + m_id + ") dependency " "(" + m_dependency_handler->get_id() + ") ready: " + std::to_string(m_dependency_handler->is_state_built()), sinsp_logger::SEV_TRACE); return m_dependency_handler->is_state_built(); #else return true; #endif // HAS_CAPTURE } void k8s_handler::process_events() { if(dependency_ready()) { unsigned counter = 0; for(auto evt = m_events.begin(); evt != m_events.end();) { m_state_processing_started = true; if(++counter >= get_max_messages()) { break; } if(*evt && !(*evt)->isNull()) { if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log("k8s_handler (" + m_id + ") processing event data:\n" + json_as_string(*(*evt)), sinsp_logger::SEV_TRACE); } #ifdef HAS_CAPTURE if(m_is_captured) { m_state->enqueue_capture_event(**evt); } #endif // HAS_CAPTURE handle_json(std::move(**evt)); } else { g_logger.log("k8s_handler (" + m_id + ") error " + #ifdef HAS_CAPTURE "(" + uri(m_url).to_string(false) + ") " + #endif // HAS_CAPTURE (!(*evt) ? "data is null." : ((*evt)->isNull() ? "JSON is null." : "Unknown")), sinsp_logger::SEV_ERROR); } evt = m_events.erase(evt); } if(!m_state_built && m_state_processing_started && !m_events.size()) { m_state_built = true; } } } void k8s_handler::set_event_json(json_ptr_t json, const std::string&) { g_logger.log("k8s_handler adding event, (" + m_id + ") has " + std::to_string(m_events.size()) #ifdef HAS_CAPTURE + " events from " + uri(m_url).to_string(false) #endif // HAS_CAPTURE , sinsp_logger::SEV_TRACE); // empty JSON is fine here; if there are no entities, state and first watch will pass nothing in here // null is checked when processing m_events.emplace_back(json); g_logger.log("k8s_handler added event, (" + m_id + ") has " + std::to_string(m_events.size()) #ifdef HAS_CAPTURE + " events from " + uri(m_url).to_string(false) #endif // HAS_CAPTURE , sinsp_logger::SEV_TRACE); #ifdef HAS_CAPTURE if(!m_resp_recvd) { m_resp_recvd = true; } #endif // HAS_CAPTURE } k8s_pair_list k8s_handler::extract_object(const Json::Value& object) { k8s_pair_list entry_list; if(!object.isNull() && object.isObject()) { Json::Value::Members members = object.getMemberNames(); for (auto& member : members) { const Json::Value& val = object[member]; if(!val.isNull() && val.isString()) { entry_list.emplace_back(k8s_pair_t(member, val.asString())); } } } return entry_list; } std::string k8s_handler::name() const { std::string n; #ifdef HAS_CAPTURE std::string::size_type slash_pos = m_path.rfind('/'); std::string::size_type qm_pos = m_path.rfind('?'); std::string::size_type length = (qm_pos == std::string::npos) ? std::string::npos : qm_pos - slash_pos - 1; if((slash_pos != std::string::npos) && (++slash_pos < m_path.size())) { n = m_path.substr(slash_pos, length); } #endif // HAS_CAPTURE return n; } void k8s_handler::handle_error(const msg_data& data, const Json::Value& root, bool log) { if(log) { log_error(data, root); } } void k8s_handler::log_error(const msg_data& data, const Json::Value& json) { #ifdef HAS_CAPTURE std::string unk_err = "Unknown."; std::ostringstream os;; os << "K8S server reported " << name() << " error for [" + uri(m_url).to_string(false) + m_path + "]: "; if(!json.isNull()) { os << std::endl << json.toStyledString(); unk_err.clear(); m_error.reset(new k8s_api_error(data, json)); } os << unk_err; g_logger.log(os.str(), sinsp_logger::SEV_ERROR); #endif // HAS_CAPTURE } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/k8s_handler.h000066400000000000000000000216721352731327100213110ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_handler.h // #pragma once #include "json/json.h" #include "sinsp_auth.h" #include "socket_collector.h" #include "k8s_state.h" #include "k8s_api_error.h" #include class sinsp; class k8s_handler { public: typedef k8s_component::msg_reason msg_reason; typedef k8s_component::msg_data msg_data; typedef std::shared_ptr ptr_t; typedef std::vector uri_list_t; typedef std::shared_ptr json_ptr_t; typedef std::shared_ptr api_error_ptr; #ifdef HAS_CAPTURE typedef sinsp_ssl::ptr_t ssl_ptr_t; typedef sinsp_bearer_token::ptr_t bt_ptr_t; typedef socket_data_handler handler_t; typedef handler_t::ptr_t handler_ptr_t; typedef socket_collector collector_t; typedef std::shared_ptr> collector_ptr_t; #endif // HAS_CAPTURE static const int default_timeout_ms = 1000L; k8s_handler(const std::string& id, bool is_captured, #ifdef HAS_CAPTURE std::string url, const std::string& path, const std::string& state_filter, const std::string& event_filter, const std::string& null_filter, collector_ptr_t collector = nullptr, const std::string& http_version = "1.1", int timeout_ms = default_timeout_ms, ssl_ptr_t ssl = nullptr, bt_ptr_t bt = nullptr, bool watch = true, bool connect = true, ptr_t dependency_handler = nullptr, bool blocking_socket = false, #endif // HAS_CAPTURE unsigned max_messages = ~0, k8s_state_t* state = nullptr); virtual ~k8s_handler(); bool connection_error() const; bool is_alive() const; bool ready() const; void set_event_json(json_ptr_t json, const std::string&); const std::string& get_id() const; #ifdef HAS_CAPTURE handler_ptr_t handler(); #endif // HAS_CAPTURE std::string get_url() const; void collect_data(); void set_machine_id(const std::string& machine_id); const std::string& get_machine_id() const; bool is_state_built() const; std::string name() const; api_error_ptr error() const; virtual void handle_json(Json::Value&& root); unsigned get_max_messages() const; void set_max_messages(unsigned max_msgs); protected: typedef std::unordered_set ip_addr_list_t; virtual bool handle_component(const Json::Value& json, const msg_data* data = 0) = 0; msg_data get_msg_data(const std::string& evt, const std::string& type, const Json::Value& root); #ifdef HAS_CAPTURE static bool is_ip_address(const std::string& addr); #endif // HAS_CAPTURE k8s_pair_list extract_object(const Json::Value& object); template void handle_selectors(T& component, const Json::Value& selector) { if(!selector.isNull()) { component.set_selectors(extract_object(selector)); } else { g_logger.log("K8s Replication Controller: Null selector object.", sinsp_logger::SEV_ERROR); } } void log_event(const msg_data& data); void handle_error(const msg_data& data, const Json::Value& root, bool log = true); void log_error(const msg_data& data, const Json::Value& root); void log_not_found(const msg_data& data) const; k8s_state_t* m_state = nullptr; bool m_state_built = false; bool m_data_received = false; static std::string ERROR_FILTER; private: typedef void (k8s_handler::*callback_func_t)(json_ptr_t, const std::string&); typedef std::vector event_list_t; #ifdef HAS_CAPTURE static ip_addr_list_t hostname_to_ip(const std::string& hostname); #endif // HAS_CAPTURE bool connect(); void make_http(); void send_data_request(); void receive_response(); void check_enabled(); void check_state(); void check_collector_status(); void process_events(); const std::string& translate_name(const std::string& event_name); bool dependency_ready() const; std::string m_id; std::string m_machine_id; #ifdef HAS_CAPTURE collector_ptr_t m_collector; handler_ptr_t m_handler; std::string m_path; std::string m_state_filter; std::string m_event_filter; std::string m_null_filter; std::string* m_filter; long m_timeout_ms; std::string m_url; bool m_req_sent = false; bool m_resp_recvd = false; json_query m_jq; std::string m_http_version; ssl_ptr_t m_ssl; bt_ptr_t m_bt; // some handlers only fetch state and die by design (eg. api or extensions handlers // have no need to continuously watch for updates) // this flag indicates whether handler should continue to watch after receiving // the initial state bool m_watch; bool m_watching = false; // indication of being in watch mode // flag indicating whether to connect to K8s API server (no connection needed when // replaying capture) bool m_connect; // k8s_handler on which this handler depends; the dependency handler must not be null and // it must have its state fully built before this handler can begin building its own state ptr_t m_dependency_handler; bool m_blocking_socket = false; #endif // HAS_CAPTURE // limits the number of messages handled in single cycle unsigned m_max_messages = ~0; bool m_state_processing_started = false; event_list_t m_events; // error indicating something went wrong with the K8s component handled by this handler // this error is later examined by k8s::check_components() and if it is // HTTP status > 400, one of the following actions is taken: // - if component is critical for consistent k8s state (eg. namespace, node, pod), // exception is thrown and, consequently, the whole k8s framework will be destroyed // - if component is not critical (eg. extensions like daemonset or deployment), // error is logged and handler is destroyed, but the k8s framework continues to // exist without it, only receiving data for existing components api_error_ptr m_error; // this capture flag does not indicate whether we are in global capture mode, // it is only an indication of whether this handler data should be captured // at all (eg. there is no need to capture api or extensions detection data) // // global capture flag is checked in the k8s state call bool m_is_captured = false; bool m_connect_logged = false; }; inline unsigned k8s_handler::get_max_messages() const { return m_max_messages; } inline void k8s_handler::set_max_messages(unsigned max_msgs) { m_max_messages = max_msgs; } #ifdef HAS_CAPTURE inline k8s_handler::handler_ptr_t k8s_handler::handler() { return m_handler; } #endif // HAS_CAPTURE inline std::string k8s_handler::get_url() const { #ifdef HAS_CAPTURE return m_url; #else return ""; #endif } inline const std::string& k8s_handler::get_id() const { return m_id; } inline void k8s_handler::set_machine_id(const std::string& machine_id) { m_machine_id = machine_id; } inline const std::string& k8s_handler::get_machine_id() const { return m_machine_id; } inline bool k8s_handler::ready() const { return m_data_received; } inline bool k8s_handler::is_state_built() const { return m_state_built; } inline void k8s_handler::log_event(const msg_data& data) { g_logger.log("K8s " + data.get_reason_desc() + ' ' + data.m_kind + ' ' + data.m_name + " [" + data.m_uid + "]", sinsp_logger::SEV_DEBUG); } inline void k8s_handler::log_not_found(const msg_data& data) const { g_logger.log("K8s " + name() + " not found [" + data.m_uid + "]: " + data.m_name, sinsp_logger::SEV_ERROR); } inline k8s_handler::api_error_ptr k8s_handler::error() const { return m_error; } // This dummy class serves only as a dependency stand-in for handlers // which have no dependencies (eg. nodes handler, which is first populated // into the state and has no dependency; or special-purpose handlers, // such as delegator, api handler etc), but the logic requires a non-null // pointer to handler to determine whether dependency is ready; to avoid // special-casing eg. nodes handler all over the place, we use this dummy // liar, always returning true for its state being built, as the dependency class k8s_dummy_handler : public k8s_handler { public: k8s_dummy_handler(): k8s_handler("k8s_dummy_handler", false, #ifdef HAS_CAPTURE "", "", "", "", "", nullptr, "", 0, nullptr, nullptr, false, false, nullptr, false, #endif // HAS_CAPTURE ~0, nullptr) { m_state_built = true; } private: virtual bool handle_component(const Json::Value& json, const msg_data* data = 0) { return false; }; }; sysdig-0.26.4/userspace/libsinsp/k8s_namespace_handler.cpp000066400000000000000000000064551352731327100236620ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_namespace_handler.cpp // #ifndef CYGWING_AGENT #include "k8s_namespace_handler.h" #include "sinsp.h" #include "sinsp_int.h" // filters normalize state and event JSONs, so they can be processed generically: // event is turned into a single-entry array, state is turned into an array of ADDED events std::string k8s_namespace_handler::EVENT_FILTER = "{" " type: .type," " apiVersion: .object.apiVersion," " kind: .object.kind," " items:" " [ .object |" " {" " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " labels: .metadata.labels" " }" " ]" "}"; std::string k8s_namespace_handler::STATE_FILTER = "{" " type: \"ADDED\"," " apiVersion: .apiVersion," " kind: \"Namespace\"," " items:" " [" " .items[] |" " {" " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " labels: .metadata.labels" " }" " ]" "}"; k8s_namespace_handler::k8s_namespace_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector ,std::string url ,const std::string& http_version ,ssl_ptr_t ssl ,bt_ptr_t bt ,bool connect ,bool blocking_socket #endif // HAS_CAPTURE ): k8s_handler("k8s_namespace_handler", true, #ifdef HAS_CAPTURE url, "/api/v1/namespaces", STATE_FILTER, EVENT_FILTER, "", collector, http_version, 1000L, ssl, bt, true, connect, dependency_handler, blocking_socket, #endif // HAS_CAPTURE ~0, &state) { } k8s_namespace_handler::~k8s_namespace_handler() { } bool k8s_namespace_handler::handle_component(const Json::Value& json, const msg_data* data) { if(data) { if(m_state) { if((data->m_reason == k8s_component::COMPONENT_ADDED) || (data->m_reason == k8s_component::COMPONENT_MODIFIED)) { k8s_ns_t& ns = m_state->get_component(m_state->get_namespaces(), data->m_name, data->m_uid); k8s_pair_list entries = k8s_component::extract_object(json, "labels"); if(entries.size() > 0) { ns.set_labels(std::move(entries)); } } else if(data->m_reason == k8s_component::COMPONENT_DELETED) { if(!m_state->delete_component(m_state->get_namespaces(), data->m_uid)) { log_not_found(*data); return false; } } else if(data->m_reason != k8s_component::COMPONENT_ERROR) { g_logger.log(std::string("Unsupported K8S " + name() + " event reason: ") + std::to_string(data->m_reason), sinsp_logger::SEV_ERROR); return false; } } else { throw sinsp_exception("K8s node handler: state is null."); } } else { throw sinsp_exception("K8s namespace handler: data is null."); } return true; } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/k8s_namespace_handler.h000066400000000000000000000024311352731327100233150ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_namespace_handler.h // #pragma once #include "json/json.h" #include "sinsp_auth.h" #include "k8s_handler.h" class sinsp; class k8s_namespace_handler : public k8s_handler { public: k8s_namespace_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector = nullptr ,std::string url = "" ,const std::string& http_version = "1.1" ,ssl_ptr_t ssl = 0 ,bt_ptr_t bt = 0 ,bool connect = true ,bool blocking_socket = false #endif // HAS_CAPTURE ); ~k8s_namespace_handler(); private: static std::string EVENT_FILTER; static std::string STATE_FILTER; bool handle_component(const Json::Value& json, const msg_data* data = 0); }; sysdig-0.26.4/userspace/libsinsp/k8s_net.cpp000066400000000000000000000213001352731327100210010ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_net.cpp // #ifndef CYGWING_AGENT #ifdef HAS_CAPTURE #include "k8s_net.h" #include "k8s_component.h" #include "k8s_node_handler.h" #include "k8s_namespace_handler.h" #include "k8s_pod_handler.h" #include "k8s_replicationcontroller_handler.h" #include "k8s_replicaset_handler.h" #include "k8s_service_handler.h" #include "k8s_daemonset_handler.h" #include "k8s_deployment_handler.h" #include "k8s_event_handler.h" #include "k8s.h" #include "sinsp.h" #include "sinsp_int.h" #include #include #include k8s_net::k8s_net(k8s& kube, k8s_state_t& state, const std::string& uri, ssl_ptr_t ssl, bt_ptr_t bt, filter_ptr_t event_filter, bool blocking_sockets) : m_state(state), m_collector(std::make_shared()), m_uri(uri), m_ssl(ssl), m_bt(bt), m_stopped(true), m_blocking_sockets(blocking_sockets), m_event_filter(event_filter) { } k8s_net::~k8s_net() { cleanup(); } void k8s_net::cleanup() { stop_watching(); m_handlers.clear(); } void k8s_net::watch() { for(auto it = m_handlers.cbegin(); it != m_handlers.cend();) { k8s_component::type comp_type = it->first; if(it->second) { if(it->second->connection_error()) { if(k8s_component::is_critical(comp_type)) { throw sinsp_exception("K8s: " + k8s_component::get_name(comp_type) + " connection error."); } else { g_logger.log("K8s: " + k8s_component::get_name(comp_type) + " connection error, removing component.", sinsp_logger::SEV_WARNING); if(m_collector->has(it->second->handler())) { m_collector->remove(it->second->handler()); } m_handlers.erase(it++); g_logger.log("K8s: " + k8s_component::get_name(comp_type) + " removed from watched endpoints.", sinsp_logger::SEV_INFO); } } else { it->second->collect_data(); ++it; } } else { g_logger.log("K8s: " + k8s_component::get_name(comp_type) + " handler is null.", sinsp_logger::SEV_WARNING); ++it; } } } void k8s_net::stop_watching() { if(!m_stopped) { m_stopped = true; m_collector->remove_all(); } } k8s_net::handler_ptr_t k8s_net::get_dependency_handler(const handler_map_t& handlers, const k8s_component::type& component) { switch(component) { case k8s_component::K8S_NODES: return std::make_shared(); case k8s_component::K8S_NAMESPACES: return get_handler(handlers, k8s_component::K8S_NODES); case k8s_component::K8S_PODS: return get_handler(handlers, k8s_component::K8S_NAMESPACES); case k8s_component::K8S_REPLICATIONCONTROLLERS: return get_handler(handlers, k8s_component::K8S_PODS); case k8s_component::K8S_SERVICES: return get_handler(handlers, k8s_component::K8S_PODS); case k8s_component::K8S_REPLICASETS: return get_handler(handlers, k8s_component::K8S_PODS); case k8s_component::K8S_DAEMONSETS: return get_handler(handlers, k8s_component::K8S_PODS); case k8s_component::K8S_DEPLOYMENTS: return get_handler(handlers, k8s_component::K8S_PODS); case k8s_component::K8S_EVENTS: return std::make_shared(); case k8s_component::K8S_COMPONENT_COUNT: default: break; } throw sinsp_exception(std::string("Invalid K8s component type:") + std::to_string(component)); } k8s_net::handler_ptr_t k8s_net::get_dependency_handler(const handler_map_t& handlers, const k8s_component::type_map::value_type& component) { return get_dependency_handler(handlers, component.first); } bool k8s_net::has_dependency(const k8s_component::type_map::value_type& component) { auto it = get_dependency_handler(m_handlers, component); return (it && it->is_state_built()); } k8s_net::handler_ptr_t k8s_net::make_handler(k8s_state_t& state, const k8s_component::type component, bool connect, handler_ptr_t dep, collector_ptr_t collector, const std::string& urlstr, ssl_ptr_t ssl, bt_ptr_t bt, bool blocking, filter_ptr_t event_filter) { switch(component) { case k8s_component::K8S_NODES: return std::make_shared(state, dep, collector, urlstr, "1.1", ssl, bt, connect, blocking); case k8s_component::K8S_NAMESPACES: return std::make_shared(state, dep, collector, urlstr, "1.1", ssl, bt, connect, blocking); case k8s_component::K8S_PODS: return std::make_shared(state, dep, collector, urlstr, "1.1", ssl, bt, connect, blocking); case k8s_component::K8S_REPLICATIONCONTROLLERS: return std::make_shared(state, dep, collector, urlstr, "1.1", ssl, bt, connect, blocking); case k8s_component::K8S_REPLICASETS: return std::make_shared(state, dep, collector, urlstr, "1.1", ssl, bt, connect, blocking); case k8s_component::K8S_SERVICES: return std::make_shared(state, dep, collector, urlstr, "1.1", ssl, bt, connect, blocking); case k8s_component::K8S_DAEMONSETS: return std::make_shared(state, dep, collector, urlstr, "1.1", ssl, bt, connect, blocking); case k8s_component::K8S_DEPLOYMENTS: return std::make_shared(state, dep, collector, urlstr, "1.1", ssl, bt, connect, blocking); case k8s_component::K8S_EVENTS: return std::make_shared(state, dep, collector, urlstr, "1.1", ssl, bt, connect, blocking, event_filter); case k8s_component::K8S_COMPONENT_COUNT: default: return nullptr; } return nullptr; } void k8s_net::add_handler(const k8s_component::type_map::value_type& component) { if(!has_handler(component)) { handler_ptr_t handler = make_handler(m_state, component.first, true, get_dependency_handler(m_handlers, component), m_collector, m_uri.to_string(), m_ssl, m_bt, m_blocking_sockets, m_event_filter); if(handler) { if(!m_machine_id.empty()) { handler->set_machine_id(m_machine_id); } else if(handler->name() == "events") { g_logger.log("K8s machine ID (MAC) is empty - scope may not be available for " + handler->name(), sinsp_logger::SEV_WARNING); } m_handlers[component.first] = handler; } else { std::ostringstream os; os << "K8s: invalid component type encountered while creating handler: " << component.second << " (" << std::to_string(component.first) << ')'; if(k8s_component::is_critical(component)) { throw sinsp_exception(os.str()); } else { g_logger.log(os.str(), sinsp_logger::SEV_ERROR); } } g_logger.log("K8s: created " + k8s_component::get_name(component) + " handler.", sinsp_logger::SEV_INFO); } else { g_logger.log("K8s: component " + k8s_component::get_name(component) + " already exists.", sinsp_logger::SEV_TRACE); } } #else // !HAS_CAPTURE #include "k8s_component.h" #include "k8s_node_handler.h" #include "k8s_namespace_handler.h" #include "k8s_pod_handler.h" #include "k8s_replicationcontroller_handler.h" #include "k8s_replicaset_handler.h" #include "k8s_service_handler.h" #include "k8s_daemonset_handler.h" #include "k8s_deployment_handler.h" #include "k8s_event_handler.h" namespace k8s_net { k8s_handler::ptr_t make_handler(k8s_state_t& state, const k8s_component::type component, bool /*connect*/) { switch(component) { case k8s_component::K8S_NODES: return std::make_shared(state); case k8s_component::K8S_NAMESPACES: return std::make_shared(state); case k8s_component::K8S_PODS: return std::make_shared(state); case k8s_component::K8S_REPLICATIONCONTROLLERS: return std::make_shared(state); case k8s_component::K8S_REPLICASETS: return std::make_shared(state); case k8s_component::K8S_SERVICES: return std::make_shared(state); case k8s_component::K8S_DAEMONSETS: return std::make_shared(state); case k8s_component::K8S_DEPLOYMENTS: return std::make_shared(state); case k8s_component::K8S_EVENTS: return std::make_shared(state); case k8s_component::K8S_COMPONENT_COUNT: default: return nullptr; } return nullptr; } } #endif // HAS_CAPTURE #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/k8s_net.h000066400000000000000000000117411352731327100204560ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_net.h // // connects and gets the data from k8s_net REST API interface // #pragma once #ifdef HAS_CAPTURE #include "k8s_component.h" #include "k8s_event_data.h" #include "k8s_handler.h" #include "k8s_event_data.h" #include "uri.h" #include "sinsp_curl.h" #include #include class k8s; class k8s_net { public: typedef sinsp_ssl::ptr_t ssl_ptr_t; typedef sinsp_bearer_token::ptr_t bt_ptr_t; typedef k8s_component::ext_list_ptr_t ext_list_ptr_t; typedef user_event_filter_t::ptr_t filter_ptr_t; typedef k8s_handler::ptr_t handler_ptr_t; typedef k8s_handler::collector_t collector_t; typedef k8s_handler::collector_ptr_t collector_ptr_t; k8s_net(k8s& kube, k8s_state_t& state, const std::string& uri = "http://localhost:80", ssl_ptr_t ssl = nullptr, bt_ptr_t bt = nullptr, filter_ptr_t event_filter = nullptr, bool blocking_sockets = false); ~k8s_net(); static handler_ptr_t make_handler(k8s_state_t& state, const k8s_component::type component, bool connect = true, handler_ptr_t dep = std::make_shared(), collector_ptr_t collector = nullptr, const std::string& urlstr = "", ssl_ptr_t ssl = nullptr, bt_ptr_t bt = nullptr, bool blocking = false, filter_ptr_t event_filter = nullptr); void add_handler(const k8s_component::type_map::value_type& component); bool has_handler(const k8s_component::type_map::value_type& component); bool has_dependency(const k8s_component::type_map::value_type& component); bool is_state_built(const k8s_component::type_map::value_type& component); void watch(); void stop_watching(); bool is_healthy() const; void set_machine_id(const std::string& machine_id); const std::string& get_machine_id() const; typedef k8s_handler::handler_t handler_t; typedef std::map handler_map_t; const handler_map_t& handlers() const; static handler_ptr_t get_handler(const handler_map_t& handlers, k8s_component::type component); static handler_ptr_t get_handler(const handler_map_t& handlers, const k8s_component::type_map::value_type& component); static handler_ptr_t get_dependency_handler(const handler_map_t& handlers, const k8s_component::type_map::value_type& component); static handler_ptr_t get_dependency_handler(const handler_map_t& handlers, const k8s_component::type& component); private: void init(); bool is_secure(); void cleanup(); k8s_state_t& m_state; collector_ptr_t m_collector; uri m_uri; ssl_ptr_t m_ssl; bt_ptr_t m_bt; bool m_stopped; handler_map_t m_handlers; bool m_blocking_sockets = false; filter_ptr_t m_event_filter; std::string m_machine_id; }; inline bool k8s_net::is_secure() { return m_uri.get_scheme() == "https"; } inline bool k8s_net::is_healthy() const { if(m_collector) { if (m_collector->get_steady_state()) { return m_collector->subscription_count() == static_cast(m_handlers.size()); } else { return true; } } else { return false; } } inline bool k8s_net::has_handler(const k8s_component::type_map::value_type& component) { auto it = m_handlers.find(component.first); return (it != m_handlers.end()) && it->second; } inline k8s_net::handler_ptr_t k8s_net::get_handler(const handler_map_t& handlers, k8s_component::type component) { auto it = handlers.find(component); if(it != handlers.end()) { return it->second; } return nullptr; } inline k8s_net::handler_ptr_t k8s_net::get_handler(const handler_map_t& handlers, const k8s_component::type_map::value_type& component) { return get_handler(handlers, component.first); } inline bool k8s_net::is_state_built(const k8s_component::type_map::value_type& component) { const auto& it = m_handlers.find(component.first); if(it != m_handlers.end()) { return it->second && it->second->is_state_built(); } return false; } inline void k8s_net::set_machine_id(const std::string& machine_id) { m_machine_id = machine_id; } inline const std::string& k8s_net::get_machine_id() const { return m_machine_id; } inline const k8s_net::handler_map_t& k8s_net::handlers() const { return m_handlers; } #else // !HAS_CAPTURE #include "k8s_component.h" #include "k8s_handler.h" namespace k8s_net { k8s_handler::ptr_t make_handler(k8s_state_t& state, const k8s_component::type component, bool /*connect*/); } #endif // HAS_CAPTURE sysdig-0.26.4/userspace/libsinsp/k8s_node_handler.cpp000066400000000000000000000073241352731327100226470ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_node_handler.cpp // #ifndef CYGWING_AGENT #include "k8s_node_handler.h" #include "sinsp.h" #include "sinsp_int.h" // filters normalize state and event JSONs, so they can be processed generically: // event is turned into a single-entry array, state is turned into an array of ADDED events std::string k8s_node_handler::EVENT_FILTER = "{" " type: .type," " apiVersion: .object.apiVersion," " kind: .object.kind," " items:" " [" " .object |" " {" " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " labels: .metadata.labels," " addresses: [.status.addresses[].address] | unique" " }" " ]" "}"; std::string k8s_node_handler::STATE_FILTER = "{" " type: \"ADDED\"," " apiVersion: .apiVersion," " kind: \"Node\", " " items:" " [" " .items[] | " " {" " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " labels: .metadata.labels," " addresses: [.status.addresses[].address] | unique" " }" " ]" "}"; k8s_node_handler::k8s_node_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector ,std::string url ,const std::string& http_version ,ssl_ptr_t ssl ,bt_ptr_t bt ,bool connect ,bool blocking_socket #endif // HAS_CAPTURE ): k8s_handler("k8s_node_handler", true, #ifdef HAS_CAPTURE url, "/api/v1/nodes", STATE_FILTER, EVENT_FILTER, "", collector, http_version, 1000L, ssl, bt, true, connect, dependency_handler, blocking_socket, #endif // HAS_CAPTURE ~0, &state) { } k8s_node_handler::~k8s_node_handler() { } bool k8s_node_handler::handle_component(const Json::Value& json, const msg_data* data) { if(data) { if(m_state) { if((data->m_reason == k8s_component::COMPONENT_ADDED) || (data->m_reason == k8s_component::COMPONENT_MODIFIED)) { k8s_node_t& node = m_state->get_component(m_state->get_nodes(), data->m_name, data->m_uid); k8s_node_t::host_ip_list addresses; k8s_component::extract_string_array(json["addresses"], addresses); if(addresses.size() > 0) { node.set_host_ips(std::move(addresses)); } else { g_logger.log("K8s Node handler: Can not obtain IP address(es) for node" + data->m_name + '[' + data->m_uid + ']', sinsp_logger::SEV_ERROR); } k8s_pair_list entries = k8s_component::extract_object(json, "labels"); if(entries.size() > 0) { node.set_labels(std::move(entries)); } } else if(data->m_reason == k8s_component::COMPONENT_DELETED) { if(!m_state->delete_component(m_state->get_nodes(), data->m_uid)) { log_not_found(*data); return false; } } else if(data->m_reason != k8s_component::COMPONENT_ERROR) { g_logger.log(std::string("Unsupported K8S " + name() + " event reason: ") + std::to_string(data->m_reason), sinsp_logger::SEV_ERROR); return false; } } else { throw sinsp_exception("K8s node handler: state is null."); } } else { throw sinsp_exception("K8s node handler: data is null."); } return true; } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/k8s_node_handler.h000066400000000000000000000024441352731327100223120ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_node_handler.h // #pragma once #include "json/json.h" #include "sinsp_auth.h" #include "k8s_handler.h" #include "k8s_state.h" class sinsp; class k8s_node_handler : public k8s_handler { public: k8s_node_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector = nullptr ,std::string url = "" ,const std::string& http_version = "1.1" ,ssl_ptr_t ssl = 0 ,bt_ptr_t bt = 0 ,bool connect = true ,bool blocking_socket = false #endif // HAS_CAPTURE ); ~k8s_node_handler(); private: static std::string EVENT_FILTER; static std::string STATE_FILTER; virtual bool handle_component(const Json::Value& json, const msg_data* data = 0); }; sysdig-0.26.4/userspace/libsinsp/k8s_pod_handler.cpp000066400000000000000000000167561352731327100225150ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_pod_handler.cpp // #ifndef CYGWING_AGENT #include "k8s_pod_handler.h" #include "sinsp.h" #include "sinsp_int.h" // filters normalize state and event JSONs, so they can be processed generically: // event is turned into a single-entry array, state is turned into an array of ADDED events std::string k8s_pod_handler::EVENT_FILTER = "{" " type: .type," " apiVersion: .object.apiVersion," " kind: .object.kind," " items:" " [" " .object |" " {" " namespace: .metadata.namespace," " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " nodeName: .spec.nodeName," " hostIP: .status.hostIP," " podIP: .status.podIP," " phase: .status.phase," " containers: .spec.containers," " containerStatuses: .status.containerStatuses," " initContainerStatuses: .status.initContainerStatuses," " labels: .metadata.labels" " }" " ]" "}"; std::string k8s_pod_handler::STATE_FILTER = "{" " type: \"ADDED\"," " apiVersion: .apiVersion," " kind: \"Pod\", " " items:" " [" " .items[] | " " {" " namespace: .metadata.namespace," " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " nodeName: .spec.nodeName," " hostIP: .status.hostIP," " podIP: .status.podIP," " phase: .status.phase," " containers: .spec.containers," " containerStatuses: .status.containerStatuses," " initContainerStatuses: .status.initContainerStatuses," " labels: .metadata.labels," " }" " ]" "}"; k8s_pod_handler::k8s_pod_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector ,std::string url ,const std::string& http_version ,ssl_ptr_t ssl ,bt_ptr_t bt ,bool connect ,bool blocking_socket #endif // HAS_CAPTURE ): k8s_handler("k8s_pod_handler", true, #ifdef HAS_CAPTURE url, "/api/v1/pods?fieldSelector=status.phase!=Failed,status.phase!=Unknown,status.phase!=Succeeded", STATE_FILTER, EVENT_FILTER, "", collector, http_version, 1000L, ssl, bt, true, connect, dependency_handler, blocking_socket, #endif // HAS_CAPTURE ~0, &state) { } k8s_pod_handler::~k8s_pod_handler() { } std::vector k8s_pod_handler::extract_pod_container_ids(const Json::Value& item) { std::vector container_list; const Json::Value& containers = item["containerStatuses"]; if(!containers.isNull()) { for (auto& container : containers) { const Json::Value& container_id = container["containerID"]; if(!container_id.isNull()) { container_list.emplace_back(container_id.asString()); } } } const Json::Value& initContainers = item["initContainerStatuses"]; if(!initContainers.isNull()) { for (auto& container : initContainers) { const Json::Value& container_id = container["containerID"]; if(!container_id.isNull()) { container_list.emplace_back(container_id.asString()); } } } return container_list; } k8s_container::list k8s_pod_handler::extract_pod_containers(const Json::Value& item) { k8s_container::list ext_containers; // Not looking for init containers here because this appears // to only be used by the k8s_service_handler for named port // resolution. Init containers can't have service ports. const Json::Value& containers = item["containers"]; if(!containers.isNull()) { for (auto& container : containers) { std::string cont_name; const Json::Value& name = container["name"]; if(!name.isNull()) { cont_name = name.asString(); } else { return ext_containers; } k8s_container::port_list cont_ports; const Json::Value& ports = container["ports"]; for(const auto& port : ports) { k8s_container::port cont_port; const Json::Value& name = port["name"]; if(!name.isNull()) { cont_port.set_name(name.asString()); } const Json::Value& cport = port["containerPort"]; if(!cport.isNull()) { cont_port.set_port(cport.asUInt()); } else { g_logger.log("Port not found, setting value to 0", sinsp_logger::SEV_WARNING); cont_port.set_port(0); } const Json::Value& protocol = port["protocol"]; if(!protocol.isNull()) { cont_port.set_protocol(protocol.asString()); } else { std::string port_name = name.isNull() ? "[NO NAME]" : name.asString(); g_logger.log("Protocol not found for port: " + port_name, sinsp_logger::SEV_WARNING); } cont_ports.push_back(cont_port); } ext_containers.emplace_back(k8s_container(cont_name, cont_ports)); } } return ext_containers; } void k8s_pod_handler::extract_pod_data(const Json::Value& item, k8s_pod_t& pod) { const Json::Value& node_name = item["nodeName"]; if(!node_name.isNull()) { std::string nn = node_name.asString(); if(!nn.empty()) { pod.set_node_name(nn); } } const Json::Value& host_ip = item["hostIP"]; if(!host_ip.isNull()) { std::string hip = host_ip.asString(); if(!hip.empty()) { pod.set_host_ip(hip); } } const Json::Value& pod_ip = item["podIP"]; if(!pod_ip.isNull()) { std::string pip = pod_ip.asString(); if(!pip.empty()) { pod.set_internal_ip(pip); } } } size_t k8s_pod_handler::extract_pod_restart_count(const Json::Value& item) { size_t restart_count = 0; const Json::Value& container_statuses = item["containerStatuses"]; if(!container_statuses.isNull()) { for (auto& status : container_statuses) { const Json::Value& rc = status["restartCount"]; if(!rc.isNull() && rc.isInt()) { restart_count += rc.asInt(); } } } return restart_count; } bool k8s_pod_handler::handle_component(const Json::Value& json, const msg_data* data) { if(data) { if(m_state) { if((data->m_reason == k8s_component::COMPONENT_ADDED) || (data->m_reason == k8s_component::COMPONENT_MODIFIED)) { k8s_pod_t& pod = m_state->get_component(m_state->get_pods(), data->m_name, data->m_uid, data->m_namespace); k8s_pair_list entries = k8s_component::extract_object(json, "labels"); if(entries.size() > 0) { pod.set_labels(std::move(entries)); } k8s_pod_t::container_id_list container_ids = extract_pod_container_ids(json); k8s_container::list containers = extract_pod_containers(json); extract_pod_data(json, pod); pod.set_restart_count(extract_pod_restart_count(json)); pod.set_container_ids(std::move(container_ids)); pod.set_containers(std::move(containers)); } else if(data->m_reason == k8s_component::COMPONENT_DELETED) { if(!m_state->delete_component(m_state->get_pods(), data->m_uid)) { log_not_found(*data); return false; } } } else if(data->m_reason != k8s_component::COMPONENT_ERROR) { g_logger.log(std::string("Unsupported K8S " + name() + " event reason: ") + std::to_string(data->m_reason), sinsp_logger::SEV_ERROR); return false; } } else { throw sinsp_exception("K8s node handler: data is null."); } return true; } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/k8s_pod_handler.h000066400000000000000000000031221352731327100221410ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_pod_handler.h // #pragma once #include "json/json.h" #include "sinsp_auth.h" #include "k8s_handler.h" #include "k8s_component.h" class sinsp; class k8s_pod_handler : public k8s_handler { public: k8s_pod_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector = nullptr ,std::string url = "" ,const std::string& http_version = "1.1" ,ssl_ptr_t ssl = 0 ,bt_ptr_t bt = 0 ,bool connect = true ,bool blocking_socket = false #endif // HAS_CAPTURE ); ~k8s_pod_handler(); static std::vector extract_pod_container_ids(const Json::Value& item); static k8s_container::list extract_pod_containers(const Json::Value& item); static void extract_pod_data(const Json::Value& item, k8s_pod_t& pod); static size_t extract_pod_restart_count(const Json::Value& item); private: static std::string EVENT_FILTER; static std::string STATE_FILTER; virtual bool handle_component(const Json::Value& json, const msg_data* data = 0); }; sysdig-0.26.4/userspace/libsinsp/k8s_replicaset_handler.cpp000066400000000000000000000102361352731327100240510ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_replicaset_handler.cpp // #ifndef CYGWING_AGENT #include "k8s_replicaset_handler.h" #include "sinsp.h" #include "sinsp_int.h" // filters normalize state and event JSONs, so they can be processed generically: // event is turned into a single-entry array, state is turned into an array of ADDED events std::string k8s_replicaset_handler::EVENT_FILTER = "{" " type: .type," " apiVersion: .object.apiVersion," " kind: .object.kind," " items:" " [" " .object |" " {" " namespace: .metadata.namespace," " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " specReplicas: .spec.replicas," " statReplicas: .status.replicas," " selector: .spec.selector.matchLabels," " labels: .metadata.labels" " }" " ]" "}"; std::string k8s_replicaset_handler::STATE_FILTER = "{" " type: \"ADDED\"," " apiVersion: .apiVersion," " kind: \"ReplicaSet\", " " items:" " [" " .items[] | " " {" " namespace: .metadata.namespace," " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " specReplicas: .spec.replicas," " statReplicas: .status.replicas," " selector: .spec.selector.matchLabels," " labels: .metadata.labels" " }" " ]" "}"; std::string k8s_replicaset_handler::NULL_FILTER = "{" " type: \"NONEXISTENT\"," " apiVersion: .apiVersion," " kind: \"ReplicaSet\", " " items: [ null ]" "}"; k8s_replicaset_handler::k8s_replicaset_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector ,std::string url ,const std::string& http_version ,ssl_ptr_t ssl ,bt_ptr_t bt ,bool connect ,bool blocking_socket #endif // HAS_CAPTURE ): k8s_handler("k8s_replicaset_handler", true, #ifdef HAS_CAPTURE url, "/apis/extensions/v1beta1/replicasets", STATE_FILTER, EVENT_FILTER, NULL_FILTER, collector, http_version, 1000L, ssl, bt, true, connect, dependency_handler, blocking_socket, #endif // HAS_CAPTURE 100, // max msgs &state) { } k8s_replicaset_handler::~k8s_replicaset_handler() { } bool k8s_replicaset_handler::handle_component(const Json::Value& json, const msg_data* data) { if(data) { if(m_state) { if((data->m_reason == k8s_component::COMPONENT_ADDED) || (data->m_reason == k8s_component::COMPONENT_MODIFIED)) { k8s_rs_t& rs = m_state->get_component(m_state->get_rss(), data->m_name, data->m_uid, data->m_namespace); k8s_pair_list entries = k8s_component::extract_object(json, "labels"); if(entries.size() > 0) { rs.set_labels(std::move(entries)); } handle_selectors(rs, json["selector"]); const Json::Value& spec = json["specReplicas"]; const Json::Value& stat = json["statReplicas"]; if(!spec.isNull() && spec.isConvertibleTo(Json::intValue) && !stat.isNull() && stat.isConvertibleTo(Json::intValue)) { rs.set_replicas(spec.asInt(), stat.asInt()); } } else if(data->m_reason == k8s_component::COMPONENT_DELETED) { if(!m_state->delete_component(m_state->get_rss(), data->m_uid)) { log_not_found(*data); return false; } } else if(data->m_reason != k8s_component::COMPONENT_ERROR) { g_logger.log(std::string("Unsupported K8S " + name() + " event reason: ") + std::to_string(data->m_reason), sinsp_logger::SEV_ERROR); return false; } } else { throw sinsp_exception("K8s node handler: state is null."); } } else { throw sinsp_exception("K8s node handler: data is null."); } return true; } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/k8s_replicaset_handler.h000066400000000000000000000025351352731327100235210ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_replicaset_handler.h // #pragma once #include "json/json.h" #include "sinsp_auth.h" #include "k8s_handler.h" #include "k8s_state.h" class sinsp; class k8s_replicaset_handler : public k8s_handler { public: k8s_replicaset_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector = nullptr ,std::string url = "" ,const std::string& http_version = "1.1" ,ssl_ptr_t ssl = 0 ,bt_ptr_t bt = 0 ,bool connect = true ,bool blocking_socket = false #endif // HAS_CAPTURE ); ~k8s_replicaset_handler(); private: static std::string EVENT_FILTER; static std::string STATE_FILTER; static std::string NULL_FILTER; virtual bool handle_component(const Json::Value& json, const msg_data* data = 0); }; sysdig-0.26.4/userspace/libsinsp/k8s_replicationcontroller_handler.cpp000066400000000000000000000104001352731327100263240ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_replicationcontroller_handler.cpp // #ifndef CYGWING_AGENT #include "k8s_replicationcontroller_handler.h" #include "sinsp.h" #include "sinsp_int.h" // filters normalize state and event JSONs, so they can be processed generically: // event is turned into a single-entry array, state is turned into an array of ADDED events std::string k8s_replicationcontroller_handler::EVENT_FILTER = "{" " type: .type," " apiVersion: .object.apiVersion," " kind: .object.kind," " items:" " [" " .object |" " {" " namespace: .metadata.namespace," " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " specReplicas: .spec.replicas," " statReplicas: .status.replicas," " selector: .spec.selector," " labels: .metadata.labels" " }" " ]" "}"; std::string k8s_replicationcontroller_handler::STATE_FILTER = "{" " type: \"ADDED\"," " apiVersion: .apiVersion," " kind: \"ReplicationController\", " " items:" " [" " .items[] | " " {" " namespace: .metadata.namespace," " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " specReplicas: .spec.replicas," " statReplicas: .status.replicas," " selector: .spec.selector," " labels: .metadata.labels" " }" " ]" "}"; std::string k8s_replicationcontroller_handler::NULL_FILTER = "{" " type: \"NONEXISTENT\"," " apiVersion: .apiVersion," " kind: \"ReplicationController\", " " items: [ null ]" "}"; k8s_replicationcontroller_handler::k8s_replicationcontroller_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector ,std::string url ,const std::string& http_version ,ssl_ptr_t ssl ,bt_ptr_t bt ,bool connect ,bool blocking_socket #endif // HAS_CAPTURE ): k8s_handler("k8s_replicationcontroller_handler", true, #ifdef HAS_CAPTURE url, "/api/v1/replicationcontrollers", STATE_FILTER, EVENT_FILTER, NULL_FILTER, collector, http_version, 1000L, ssl, bt, true, connect, dependency_handler, blocking_socket, #endif // HAS_CAPTURE 100, // max msgs &state) { } k8s_replicationcontroller_handler::~k8s_replicationcontroller_handler() { } bool k8s_replicationcontroller_handler::handle_component(const Json::Value& json, const msg_data* data) { if(data) { if(m_state) { if((data->m_reason == k8s_component::COMPONENT_ADDED) || (data->m_reason == k8s_component::COMPONENT_MODIFIED)) { k8s_rc_t& rc = m_state->get_component(m_state->get_rcs(), data->m_name, data->m_uid, data->m_namespace); k8s_pair_list entries = extract_object(json["labels"]); if(entries.size() > 0) { rc.set_labels(std::move(entries)); } handle_selectors(rc, json["selector"]); const Json::Value& spec = json["specReplicas"]; const Json::Value& stat = json["statReplicas"]; if(!spec.isNull() && spec.isConvertibleTo(Json::intValue) && !stat.isNull() && stat.isConvertibleTo(Json::intValue)) { rc.set_replicas(spec.asInt(), stat.asInt()); } } else if(data->m_reason == k8s_component::COMPONENT_DELETED) { if(!m_state->delete_component(m_state->get_rcs(), data->m_uid)) { log_not_found(*data); return false; } } else if(data->m_reason != k8s_component::COMPONENT_ERROR) { g_logger.log(std::string("Unsupported K8S " + name() + " event reason: ") + std::to_string(data->m_reason), sinsp_logger::SEV_ERROR); return false; } } else { throw sinsp_exception("K8s node handler: state is null."); } } else { throw sinsp_exception("K8s node handler: data is null."); } return true; } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/k8s_replicationcontroller_handler.h000066400000000000000000000026111352731327100257760ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_replicationcontroller_handler.h // #pragma once #include "json/json.h" #include "sinsp_auth.h" #include "k8s_handler.h" #include "k8s_state.h" class sinsp; class k8s_replicationcontroller_handler : public k8s_handler { public: k8s_replicationcontroller_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector = nullptr ,std::string url = "" ,const std::string& http_version = "1.1" ,ssl_ptr_t ssl = 0 ,bt_ptr_t bt = 0 ,bool connect = true ,bool blocking_socket = false #endif // HAS_CAPTURE ); ~k8s_replicationcontroller_handler(); private: static std::string EVENT_FILTER; static std::string STATE_FILTER; static std::string NULL_FILTER; virtual bool handle_component(const Json::Value& json, const msg_data* data = 0); }; sysdig-0.26.4/userspace/libsinsp/k8s_service_handler.cpp000066400000000000000000000147531352731327100233660ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_service_handler.cpp // #ifndef CYGWING_AGENT #include "k8s_service_handler.h" #include "sinsp.h" #include "sinsp_int.h" // filters normalize state and event JSONs, so they can be processed generically: // event is turned into a single-entry array, state is turned into an array of ADDED events std::string k8s_service_handler::EVENT_FILTER = "{" " type: .type," " apiVersion: .object.apiVersion," " kind: .object.kind," " items:" " [" " .object |" " {" " namespace: .metadata.namespace," " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " clusterIP: .spec.clusterIP," " ports: .spec.ports," " labels: .metadata.labels," " selector: .spec.selector" " }" " ]" "}"; std::string k8s_service_handler::STATE_FILTER = "{" " type: \"ADDED\"," " apiVersion: .apiVersion," " kind: \"Service\", " " items:" " [" " .items[] | " " {" " namespace: .metadata.namespace," " name: .metadata.name," " uid: .metadata.uid," " timestamp: .metadata.creationTimestamp," " clusterIP: .spec.clusterIP," " ports: .spec.ports," " labels: .metadata.labels," " selector: .spec.selector" " }" " ]" "}"; std::string k8s_service_handler::NULL_FILTER = "{" " type: \"NONEXISTENT\"," " apiVersion: .apiVersion," " kind: \"Service\", " " items: [ null ]" "}"; k8s_service_handler::k8s_service_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector ,std::string url ,const std::string& http_version ,ssl_ptr_t ssl ,bt_ptr_t bt ,bool connect ,bool blocking_socket #endif // HAS_CAPTURE ): k8s_handler("k8s_service_handler", true, #ifdef HAS_CAPTURE url, "/api/v1/services", STATE_FILTER, EVENT_FILTER, NULL_FILTER, collector, http_version, 1000L, ssl, bt, true, connect, dependency_handler, blocking_socket, #endif // HAS_CAPTURE 100, // max msgs &state) { } k8s_service_handler::~k8s_service_handler() { } void k8s_service_handler::extract_services_data(const Json::Value& json, k8s_service_t& service, const k8s_pods& pods) { if(!json.isNull()) { const Json::Value& cluster_ip = json["clusterIP"]; if(!cluster_ip.isNull()) { service.set_cluster_ip(cluster_ip.asString()); } k8s_service_t::port_list pl; const Json::Value& ports = json["ports"]; if(!ports.isNull() && ports.isArray()) { for (auto& port : ports) { k8s_service_t::net_port p; const Json::Value& json_port = port["port"]; if(!json_port.isNull()) { p.m_port = json_port.asUInt(); } const Json::Value& json_protocol = port["protocol"]; if(!json_protocol.isNull()) { p.m_protocol = json_protocol.asString(); } const Json::Value& json_target_port = port["targetPort"]; if(!json_target_port.isNull()) { if(json_target_port.isIntegral()) { p.m_target_port = json_target_port.asUInt(); } else if(json_target_port.isString()) { std::string port_name = json_target_port.asString(); std::vector pod_subset = service.get_selected_pods(pods); p.m_target_port = 0; for(const auto& pod : pod_subset) { const k8s_container::list& containers = pod->get_containers(); for(const auto& container : containers) { const k8s_container::port* container_port = container.get_port(port_name); if(container_port) { g_logger.log("K8s: found port for service [" + service.get_name() + "], " "container [" + container.get_name() + ']', sinsp_logger::SEV_DEBUG); p.m_target_port = container_port->get_port(); break; } else { g_logger.log("K8s: error while trying to determine port for service [" + service.get_name() + "]: " "no ports found for container [" + container.get_name() + "]", sinsp_logger::SEV_ERROR); p.m_target_port = 0; } } } } else { g_logger.log("Port of unknown or unsupported type.", sinsp_logger::SEV_ERROR); p.m_target_port = 0; } } const Json::Value& json_node_port = port["nodePort"]; if(!json_node_port.isNull()) { p.m_node_port = json_node_port.asUInt(); } if(p.m_port && p.m_target_port) { pl.push_back(p); } } } if(pl.size()) { service.set_port_list(std::move(pl)); } } else { g_logger.log("Error while extracting data for service [" + service.get_name() + "]: " " JSON is null.", sinsp_logger::SEV_ERROR); } } bool k8s_service_handler::handle_component(const Json::Value& json, const msg_data* data) { if(data) { if(m_state) { if((data->m_reason == k8s_component::COMPONENT_ADDED) || (data->m_reason == k8s_component::COMPONENT_MODIFIED)) { k8s_service_t& service = m_state->get_component(m_state->get_services(), data->m_name, data->m_uid, data->m_namespace); k8s_pair_list entries = k8s_component::extract_object(json, "labels"); if(entries.size() > 0) { service.set_labels(std::move(entries)); } entries = k8s_component::extract_object(json, "selector"); if(entries.size() > 0) { service.set_selectors(std::move(entries)); } extract_services_data(json, service, m_state->get_pods()); } else if(data->m_reason == k8s_component::COMPONENT_DELETED) { if(!m_state->delete_component(m_state->get_services(), data->m_uid)) { log_not_found(*data); return false; } } else if(data->m_reason != k8s_component::COMPONENT_ERROR) { g_logger.log(std::string("Unsupported K8S " + name() + " event reason: ") + std::to_string(data->m_reason), sinsp_logger::SEV_ERROR); return false; } } else { throw sinsp_exception("K8s node handler: state is null."); } } else { throw sinsp_exception("K8s node handler: data is null."); } return true; } #endifsysdig-0.26.4/userspace/libsinsp/k8s_service_handler.h000066400000000000000000000026731352731327100230310ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_service_handler.h // #pragma once #include "json/json.h" #include "sinsp_auth.h" #include "k8s_handler.h" #include "k8s_state.h" class sinsp; class k8s_service_handler : public k8s_handler { public: k8s_service_handler(k8s_state_t& state #ifdef HAS_CAPTURE ,ptr_t dependency_handler ,collector_ptr_t collector = nullptr ,std::string url = "" ,const std::string& http_version = "1.1" ,ssl_ptr_t ssl = 0 ,bt_ptr_t bt = 0 ,bool connect = true ,bool blocking_socket = false #endif // HAS_CAPTURE ); ~k8s_service_handler(); static void extract_services_data(const Json::Value& spec, k8s_service_t& service, const k8s_pods& pods); private: static std::string EVENT_FILTER; static std::string STATE_FILTER; static std::string NULL_FILTER; virtual bool handle_component(const Json::Value& json, const msg_data* data = 0); }; sysdig-0.26.4/userspace/libsinsp/k8s_state.cpp000066400000000000000000000476361352731327100213570ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_state.cpp // #ifndef CYGWING_AGENT #include "k8s_state.h" #include "k8s_pod_handler.h" #include "sinsp.h" #include "sinsp_int.h" #include #include // // state // const std::string k8s_state_t::m_docker_prefix = "docker://"; const std::string k8s_state_t::m_rkt_prefix = "rkt://"; const std::string k8s_state_t::m_containerd_prefix = "containerd://"; const std::string k8s_state_t::m_crio_prefix = "cri-o://"; const unsigned k8s_state_t::m_id_length = 12u; k8s_state_t::k8s_state_t(bool is_captured, int capture_version): m_is_captured(is_captured), m_capture_version(capture_version) { } // state/pods void k8s_state_t::update_pod(k8s_pod_t& pod, const Json::Value& item) { k8s_pod_t::container_id_list container_ids = k8s_pod_handler::extract_pod_container_ids(item); k8s_container::list containers = k8s_pod_handler::extract_pod_containers(item); k8s_pod_handler::extract_pod_data(item, pod); pod.set_restart_count(k8s_pod_handler::extract_pod_restart_count(item)); pod.set_container_ids(std::move(container_ids)); pod.set_containers(std::move(containers)); } void k8s_state_t::cache_pod(container_pod_map& map, const std::string& id, const k8s_pod_t* pod) { ASSERT(pod); ASSERT(!pod->get_name().empty()); std::string::size_type pos = id.find(m_docker_prefix); if(pos == 0) { map[id.substr(m_docker_prefix.size(), m_id_length)] = pod; return; } pos = id.find(m_rkt_prefix); if(pos == 0) { map[id.substr(m_rkt_prefix.size())] = pod; return; } pos = id.find(m_containerd_prefix); if(pos == 0) { map[id.substr(m_containerd_prefix.size(), m_id_length)] = pod; return; } pos = id.find(m_crio_prefix); if(pos == 0) { map[id.substr(m_crio_prefix.size(), m_id_length)] = pod; return; } throw sinsp_exception("Invalid container ID (expected one of: '" + m_docker_prefix + "{ID}', '" + m_rkt_prefix + "{ID}', '" + m_containerd_prefix + "{ID}', '" + m_crio_prefix + "{ID}'): " + id); } bool k8s_state_t::has_pod(k8s_pod_t& pod) { for(const auto& p : m_pods) { if(p == pod) { return true; } } return false; } // state/events void k8s_state_t::update_event(k8s_event_t& evt, const Json::Value& item) { if(!item.isNull()) { evt.update(item, *this); } else { g_logger.log("NULL K8s event received.", sinsp_logger::SEV_WARNING); } } // state/general void k8s_state_t::replace_items(k8s_component::type t, const std::string& name, const std::vector&& items) { switch (t) { case k8s_component::K8S_NODES: if(name == "labels") { m_nodes.back().m_labels = std::move(items); return; } break; case k8s_component::K8S_NAMESPACES: if(name == "labels") { m_namespaces.back().m_labels = std::move(items); return; } break; case k8s_component::K8S_PODS: if(name == "labels") { m_pods.back().m_labels = std::move(items); return; } break; case k8s_component::K8S_REPLICATIONCONTROLLERS: if(name == "labels") { m_controllers.back().m_labels = std::move(items); return; } else if(name == "selector") { m_controllers.back().m_selectors = std::move(items); return; } break; case k8s_component::K8S_REPLICASETS: if(name == "labels") { m_replicasets.back().m_labels = std::move(items); return; } else if(name == "selector") { m_replicasets.back().m_selectors = std::move(items); return; } break; case k8s_component::K8S_SERVICES: if(name == "labels") { m_services.back().m_labels = std::move(items); return; } else if(name == "selector") { m_services.back().m_selectors = std::move(items); return; } break; case k8s_component::K8S_DEPLOYMENTS: if(name == "labels") { m_deployments.back().m_labels = std::move(items); return; } else if(name == "selector") { m_deployments.back().m_selectors = std::move(items); return; } break; case k8s_component::K8S_DAEMONSETS: if(name == "labels") { m_daemonsets.back().m_labels = std::move(items); return; } else if(name == "selector") { m_daemonsets.back().m_selectors = std::move(items); return; } break; case k8s_component::K8S_EVENTS: return; case k8s_component::K8S_COMPONENT_COUNT: default: break; } std::ostringstream os; os << "Unknown component type " << static_cast(t) << " or object name " << name; throw sinsp_exception(os.str().c_str()); } k8s_component& k8s_state_t::add_common_single_value(k8s_component::type component, const std::string& name, const std::string& uid, const std::string& ns) { switch (component) { case k8s_component::K8S_NODES: return get_component(m_nodes, name, uid, ns); case k8s_component::K8S_NAMESPACES: return get_component(m_namespaces, name, uid, ns); case k8s_component::K8S_PODS: return get_component(m_pods, name, uid, ns); case k8s_component::K8S_REPLICATIONCONTROLLERS: return get_component(m_controllers, name, uid, ns); case k8s_component::K8S_REPLICASETS: return get_component(m_replicasets, name, uid, ns); case k8s_component::K8S_SERVICES: return get_component(m_services, name, uid, ns); case k8s_component::K8S_DAEMONSETS: return get_component(m_daemonsets, name, uid, ns); case k8s_component::K8S_DEPLOYMENTS: return get_component(m_deployments, name, uid, ns); case k8s_component::K8S_EVENTS: return get_component(m_events, name, uid, ns); case k8s_component::K8S_COMPONENT_COUNT: default: break; } std::ostringstream os; os << "Unknown component: " << component; throw sinsp_exception(os.str()); } k8s_node_t* k8s_state_t::get_node(const std::string& uid) { for (auto& node : m_nodes) { if(node.get_uid() == uid) { return &node; } } return nullptr; } void k8s_state_t::clear(k8s_component::type type) { if(type == k8s_component::K8S_COMPONENT_COUNT) { m_namespaces.clear(); m_nodes.clear(); m_pods.clear(); m_controllers.clear(); m_services.clear(); } else { switch (type) { case k8s_component::K8S_NODES: m_nodes.clear(); break; case k8s_component::K8S_NAMESPACES: m_namespaces.clear(); break; case k8s_component::K8S_PODS: m_pods.clear(); break; case k8s_component::K8S_REPLICATIONCONTROLLERS: m_controllers.clear(); break; case k8s_component::K8S_REPLICASETS: m_replicasets.clear(); break; case k8s_component::K8S_SERVICES: m_services.clear(); break; case k8s_component::K8S_DAEMONSETS: m_daemonsets.clear(); break; case k8s_component::K8S_DEPLOYMENTS: m_deployments.clear(); break; case k8s_component::K8S_EVENTS: m_events.clear(); break; case k8s_component::K8S_COMPONENT_COUNT: default: break; } } } // state/caching void k8s_state_t::update_cache(const k8s_component::type_map::key_type& component) { #ifndef HAS_ANALYZER switch (component) { case k8s_component::K8S_NAMESPACES: { const k8s_namespaces& nspaces = get_namespaces(); k8s_state_t::namespace_map& ns_map = get_namespace_map(); ns_map.clear(); for(const auto& ns : nspaces) { std::string ns_name = ns.get_name(); if(!is_component_cached(ns_map, ns_name, &ns)) { cache_component(ns_map, ns_name, &ns); } else { g_logger.log("Attempt to cache already cached NAMESPACE: " + ns_name, sinsp_logger::SEV_ERROR); } } } break; case k8s_component::K8S_PODS: { const k8s_pods& pods = get_pods(); k8s_state_t::container_pod_map& container_pod_map = get_container_pod_map(); container_pod_map.clear(); for(const auto& pod : pods) { const k8s_pod_t::container_id_list& c_ids = pod.get_container_ids(); for(const auto& c_id : c_ids) { if(!is_component_cached(container_pod_map, c_id, &pod)) { cache_pod(container_pod_map, c_id, &pod); } else { g_logger.log("Attempt to cache already cached POD: " + c_id, sinsp_logger::SEV_ERROR); } } } } break; case k8s_component::K8S_REPLICATIONCONTROLLERS: { const k8s_controllers& rcs = get_rcs(); const k8s_pods& pods = get_pods(); k8s_state_t::pod_rc_map& pod_ctrl_map = get_pod_rc_map(); pod_ctrl_map.clear(); for(const auto& rc : rcs) { std::vector pod_subset = rc.get_selected_pods(pods); for(auto& pod : pod_subset) { const std::string& pod_uid = pod->get_uid(); if(!is_component_cached(pod_ctrl_map, pod_uid, &rc)) { cache_component(pod_ctrl_map, pod_uid, &rc); } else { g_logger.log("Attempt to cache already cached REPLICATION CONTROLLER: " + pod_uid, sinsp_logger::SEV_ERROR); } } } } break; case k8s_component::K8S_REPLICASETS: { const k8s_replicasets& rss = get_rss(); const k8s_pods& pods = get_pods(); k8s_state_t::pod_rs_map& pod_rset_map = get_pod_rs_map(); pod_rset_map.clear(); for(const auto& rs : rss) { std::vector pod_subset = rs.get_selected_pods(pods); for(auto& pod : pod_subset) { const std::string& pod_uid = pod->get_uid(); if(!is_component_cached(pod_rset_map, pod_uid, &rs)) { cache_component(pod_rset_map, pod_uid, &rs); } else { g_logger.log("Attempt to cache already cached REPLICA SET: " + pod_uid, sinsp_logger::SEV_ERROR); } } } } break; case k8s_component::K8S_SERVICES: { const k8s_services& services = get_services(); const k8s_pods& pods = get_pods(); k8s_state_t::pod_service_map& pod_svc_map = get_pod_service_map(); pod_svc_map.clear(); for(const auto& service : services) { std::vector pod_subset = service.get_selected_pods(pods); for(auto& pod : pod_subset) { const std::string& pod_uid = pod->get_uid(); if(!is_component_cached(pod_svc_map, pod_uid, &service)) { cache_component(pod_svc_map, pod_uid, &service); } else { g_logger.log("Attempt to cache already cached SERVICE: " + pod_uid, sinsp_logger::SEV_ERROR); } } } } break; case k8s_component::K8S_DAEMONSETS: { // TODO /*const k8s_daemonsets& daemonsets = get_daemonsets(); const k8s_pods& pods = get_pods(); k8s_state_t::pod_daemonset_map& pod_svc_map = get_pod_daemonset_map(); pod_svc_map.clear(); for(const auto& daemonset : daemonsets) { std::vector pod_subset = daemonset.get_selected_pods(pods); for(auto& pod : pod_subset) { const std::string& pod_uid = pod->get_uid(); if(!is_component_cached(pod_svc_map, pod_uid, &daemonset)) { cache_component(pod_svc_map, pod_uid, &daemonset); } else { g_logger.log("Attempt to cache already cached SERVICE: " + pod_uid, sinsp_logger::SEV_ERROR); } } }*/ } break; case k8s_component::K8S_DEPLOYMENTS: { const k8s_deployments& deployments = get_deployments(); const k8s_pods& pods = get_pods(); k8s_state_t::pod_deployment_map& pod_deployment_map = get_pod_deployment_map(); pod_deployment_map.clear(); for(const auto& deployment : deployments) { std::vector pod_subset = deployment.get_selected_pods(pods); for(auto& pod : pod_subset) { const std::string& pod_uid = pod->get_uid(); if(!is_component_cached(pod_deployment_map, pod_uid, &deployment)) { cache_component(pod_deployment_map, pod_uid, &deployment); } else { g_logger.log("Attempt to cache already cached Deployment: " + pod_uid, sinsp_logger::SEV_ERROR); } } } } break; default: return; } #endif // HAS_ANALYZER } k8s_component::type k8s_state_t::component_from_json(const Json::Value& item) { const Json::Value& kind = item["kind"]; if(kind.isNull() || !kind.isString()) { throw sinsp_exception("Component kind not found in JSON."); } std::string comp = kind.asString(); if(comp == "Node") { return k8s_component::K8S_NODES; } else if(comp == "Namespace") { return k8s_component::K8S_NAMESPACES; } else if(comp == "Pod") { return k8s_component::K8S_PODS; } else if(comp == "ReplicationController") { return k8s_component::K8S_REPLICATIONCONTROLLERS; } else if(comp == "ReplicaSet") { return k8s_component::K8S_REPLICASETS; } else if(comp == "Service") { return k8s_component::K8S_SERVICES; } else if(comp == "DaemonSet") { return k8s_component::K8S_DAEMONSETS; } else if(comp == "Deployment") { return k8s_component::K8S_DEPLOYMENTS; } else if(comp == "Event") { return k8s_component::K8S_EVENTS; } throw sinsp_exception("Unknown component kind:" + comp); } const k8s_component* k8s_state_t::get_component(const std::string& uid, std::string* t) const { component_map_t::const_iterator it = m_component_map.find(uid); if(it != m_component_map.end()) { switch(it->second) { case k8s_component::K8S_NODES: if(t) { *t = "node"; } return get_component(m_nodes, uid); break; case k8s_component::K8S_NAMESPACES: if(t) { *t = "namespace"; } return get_component(m_namespaces, uid); break; case k8s_component::K8S_PODS: if(t) { *t = "pod"; } return get_component(m_pods, uid); break; case k8s_component::K8S_REPLICATIONCONTROLLERS: if(t) { *t = "replicationController"; } return get_component(m_controllers, uid); break; case k8s_component::K8S_REPLICASETS: if(t) { *t = "replicaSet"; } return get_component(m_replicasets, uid); break; case k8s_component::K8S_SERVICES: if(t) { *t = "service"; } return get_component(m_services, uid); break; case k8s_component::K8S_DAEMONSETS: if(t) { *t = "daemonSet"; } return get_component(m_daemonsets, uid); break; case k8s_component::K8S_DEPLOYMENTS: if(t) { *t = "deployment"; } return get_component(m_deployments, uid); break; case k8s_component::K8S_EVENTS: if(t) { *t = "event"; } return get_component(m_events, uid); break; default: if(t) { t->clear(); } return nullptr; } } return nullptr; } #ifdef HAS_CAPTURE void k8s_state_t::enqueue_capture_event(const Json::Value& item) { if(m_is_captured) { std::string json; if(m_capture_version == k8s_state_t::CAPTURE_VERSION_1) { json = Json::FastWriter().write(extract_capture_data(item)); } else if(m_capture_version == k8s_state_t::CAPTURE_VERSION_2) { json = Json::FastWriter().write(item); } else { throw sinsp_exception(std::string("K8s : invalid capture version (") + std::to_string(m_capture_version) + ')'); } m_capture_events.emplace_back(json); } } std::string k8s_state_t::dequeue_capture_event() { if(!m_capture_events.size()) { throw sinsp_exception("Invalid event dequeue request."); } std::string ev = m_capture_events.front(); m_capture_events.pop_front(); return ev; } #endif // HAS_CAPTURE Json::Value k8s_state_t::extract_capture_data(const Json::Value& item) { Json::Value cap_item; #ifdef HAS_CAPTURE k8s_component::type component = component_from_json(item); Json::Value ver = item["apiVersion"]; if(!ver.isNull() && ver.isString()) { cap_item["apiVersion"] = ver.asString(); } else { throw sinsp_exception("K8S capture: API version not provided."); } Json::Value type = item["type"]; if(!type.isNull() && type.isString()) { cap_item["type"] = type.asString(); } else { throw sinsp_exception("K8S capture: event type not provided."); } Json::Value kind = item["kind"]; if(!kind.isNull() && kind.isString()) { cap_item["kind"] = kind.asString(); } else { throw sinsp_exception("K8S capture: component kind not provided."); } const Json::Value& object = item["object"]; if(object.isNull()) { throw sinsp_exception("K8S capture: object not found."); } cap_item["object"] = Json::Value(); Json::Value& cap_object = cap_item["object"]; cap_object["metadata"] = Json::Value(); Json::Value& cap_metadata = cap_object["metadata"]; const Json::Value& metadata = object["metadata"]; if(metadata.isNull()) { throw sinsp_exception("K8S capture: object metadata not found."); } else { Json::Value ns = metadata["namespace"]; if(!ns.isNull()) { cap_metadata["namespace"] = ns.asString(); } cap_metadata["name"] = metadata["name"].asString(); cap_metadata["uid"] = metadata["uid"].asString(); Json::Value labels = metadata["labels"]; if(!labels.isNull()) { cap_metadata["labels"] = labels; } } Json::Value spec = object["spec"]; if(spec.isNull()) { throw sinsp_exception("K8S capture: object spec not found."); } else { Json::Value selector = spec["selector"]; if(!selector.isNull()) { cap_object["spec"] = Json::Value(); Json::Value& cap_spec = cap_object["spec"]; cap_spec["selector"] = std::move(selector); } } Json::Value status = object["status"]; if(status.isNull()) { throw sinsp_exception("K8S capture: object status not found."); } switch(component) { case k8s_component::K8S_NAMESPACES: break; case k8s_component::K8S_NODES: { cap_object["status"] = Json::Value(); Json::Value& cap_status = cap_object["status"]; cap_object["status"] = Json::Value(); cap_status["addresses"] = status["addresses"]; } break; case k8s_component::K8S_PODS: { cap_object["spec"] = Json::Value(); Json::Value& cap_spec = cap_object["spec"]; const Json::Value& node_name = spec["nodeName"]; if(!node_name.isNull()) { cap_spec["nodeName"] = node_name.asString(); } cap_object["status"] = Json::Value(); Json::Value& cap_status = cap_object["status"]; const Json::Value& host_ip = status["hostIP"]; if(!host_ip.isNull()) { cap_status["hostIP"] = host_ip.asString(); } const Json::Value& pod_ip = status["podIP"]; if(!pod_ip.isNull()) { cap_status["podIP"] = pod_ip.asString(); } if(status.isMember("containerStatuses") && status["containerStatuses"].isArray()) { for(const auto& c_status : status["containerStatuses"]) { Json::Value new_status; new_status["containerID"] = c_status["containerID"]; new_status["restartCount"] = c_status["restartCount"]; cap_status["containerStatuses"].append(new_status); } } } break; case k8s_component::K8S_SERVICES: { cap_object["spec"] = Json::Value(); Json::Value& cap_spec = cap_object["spec"]; cap_spec["clusterIP"] = spec["clusterIP"].asString(); cap_spec["ports"] = spec["ports"]; } break; case k8s_component::K8S_REPLICATIONCONTROLLERS: break; case k8s_component::K8S_REPLICASETS: break; case k8s_component::K8S_DAEMONSETS: break; case k8s_component::K8S_DEPLOYMENTS: break; case k8s_component::K8S_EVENTS: break; default: break; } std::ostringstream os; std::string nspace; if(cap_metadata.isMember("namespace")) { nspace = cap_metadata["namespace"].asString(); } os << "Capture: [" << cap_item["type"].asString() << ',' << cap_item["kind"].asString() << ',' << cap_metadata["name"].asString() << ',' << cap_metadata["uid"].asString() << ',' << nspace << ']'; g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); //g_logger.log(item.toStyledString(), sinsp_logger::SEV_DEBUG); //g_logger.log(cap_item.toStyledString(), sinsp_logger::SEV_DEBUG); #endif // HAS_CAPTURE return cap_item; } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/k8s_state.h000066400000000000000000000363111352731327100210100ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_state.h // // kubernetes state abstraction // #pragma once #include "k8s_component.h" #include "json/json.h" #include "sinsp.h" #include "sinsp_int.h" #include #include #include // // state // class k8s_state_t { public: typedef std::unordered_map namespace_map; typedef std::unordered_map container_pod_map; typedef std::unordered_multimap pod_service_map; typedef std::unordered_map pod_rc_map; typedef std::unordered_map pod_rs_map; typedef std::unordered_multimap pod_deployment_map; static const int CAPTURE_VERSION_NONE = -1; static const int CAPTURE_VERSION_1 = 1; static const int CAPTURE_VERSION_2 = 2; k8s_state_t(bool is_captured = false, int capture_version = CAPTURE_VERSION_2); // // namespaces // const k8s_namespaces& get_namespaces() const; k8s_namespaces& get_namespaces(); void push_namespace(const k8s_ns_t& ns); void emplace_namespace(k8s_ns_t&& ns); // // nodes // const k8s_nodes& get_nodes() const; k8s_nodes& get_nodes(); k8s_node_t* get_node(const std::string& uid); void push_node(const k8s_node_t& node); void emplace_node(k8s_node_t&& node); // // pods // const k8s_pods& get_pods() const; k8s_pods& get_pods(); void push_pod(const k8s_pod_t& pod); void emplace_pod(k8s_pod_t&& pod); void update_pod(k8s_pod_t& pod, const Json::Value& item); bool has_pod(k8s_pod_t& pod); const k8s_pod_t::container_id_list& get_pod_container_ids(k8s_pod_t& pod); // // replication controllers // const k8s_controllers& get_rcs() const; k8s_controllers& get_rcs(); void push_rc(const k8s_rc_t& rc); void emplace_rc(k8s_rc_t&& rc); // // replica sets // const k8s_replicasets& get_rss() const; k8s_replicasets& get_rss(); void push_rs(const k8s_rs_t& rs); void emplace_rs(k8s_rs_t&& rs); // // services // const k8s_services& get_services() const; k8s_services& get_services(); void push_service(const k8s_service_t& service); void emplace_service(k8s_service_t&& service); // // daemonsets // const k8s_daemonsets& get_daemonsets() const; k8s_daemonsets& get_daemonsets(); void push_daemonset(const k8s_daemonset_t& daemonset); void emplace_daemonset(k8s_daemonset_t&& daemonset); // // deployments // const k8s_deployments& get_deployments() const; k8s_deployments& get_deployments(); void push_deployment(const k8s_deployment_t& deployment); void emplace_deployment(k8s_deployment_t&& deployment); // // events // const k8s_events& get_events() const; k8s_events& get_events(); void clear_events(); void push_event(const k8s_event_t& evt); void emplace_event(k8s_event_t&& evt); void update_event(k8s_event_t& evt, const Json::Value& item); // // general // void replace_items(k8s_component::type t, const std::string& name, const std::vector&& items); k8s_component& add_common_single_value(k8s_component::type component, const std::string& name, const std::string& uid, const std::string& ns); void set_last_pod_node_name(const std::string& name); void set_last_pod_host_ip(const std::string& host_ip); void set_last_pod_internal_ip(const std::string& internal_ip); void add_last_node_ip(std::string&& ip); void add_last_pod_container_id(std::string&& container_id); // Returns true if component exists, false otherwise. template bool has(const C& components, const std::string& uid) const { for (auto& comp : components) { if(uid == comp.get_uid()) { return true; } } return false; } bool has(const std::string& uid) const { return get_component(uid) != nullptr; } // Returns a pointer to existing component, if it exists. // If component does not exist, it returns null pointer. template T* get_component(C& components, const std::string& uid) { for (auto& comp : components) { if(comp.get_uid() == uid) { return ∁ } } return 0; } template const T* get_component(const C& components, const std::string& uid) const { for (const auto& comp : components) { if(comp.get_uid() == uid) { return ∁ } } return 0; } template T& add_component(C& container, const std::string& name, const std::string& uid, const std::string& ns = "") { m_component_map[uid] = T::COMPONENT_TYPE; container.emplace_back(std::move(T(name, uid, ns))); return container.back(); } // Returns the reference to existing component, if it exists. // If component does not exist, it emplaces it to the back of the // container and returns the reference of the added component. template T& get_component(C& container, const std::string& name, const std::string& uid, const std::string& ns = "") { for (auto& comp : container) { if(comp.get_uid() == uid) { return comp; } } return add_component(container, name, uid, ns); } template bool delete_component(C& components, const std::string& uid) { for (typename C::iterator component = components.begin(), end = components.end(); component != end; ++component) { if(component->get_uid() == uid) { components.erase(component); m_component_map.erase(uid); return true; } } return false; } void clear(k8s_component::type type = k8s_component::K8S_COMPONENT_COUNT); // // cached lookup support // // any component by uid const k8s_component* get_component(const std::string& uid, std::string* t = 0) const; #ifndef HAS_ANALYZER // pod by container; const k8s_pod_t* get_pod(const std::string& container) const { container_pod_map::const_iterator it = m_container_pods.find(container); if(it != m_container_pods.end()) { return it->second; } return 0; } const namespace_map& get_namespace_map() const { return m_namespace_map; } const container_pod_map& get_container_pod_map() const { return m_container_pods; } const pod_service_map& get_pod_service_map() const { return m_pod_services; } const pod_rc_map& get_pod_rc_map() const { return m_pod_rcs; } const pod_rs_map& get_pod_rs_map() const { return m_pod_rss; } const pod_deployment_map& get_pod_deployment_map() const { return m_pod_deployments; } #endif // HAS_ANALYZER void set_capture_version(int version); int get_capture_version() const; #ifdef HAS_CAPTURE typedef std::deque event_list_t; const event_list_t& get_capture_events() const { return m_capture_events; } void enqueue_capture_event(const Json::Value& item); std::string dequeue_capture_event(); #endif // HAS_CAPTURE private: void update_cache(const k8s_component::type_map::key_type& component); static k8s_component::type component_from_json(const Json::Value& item); static Json::Value extract_capture_data(const Json::Value& item); template const typename C::mapped_type* get_component(const C& map, const std::string& key) { typename C::const_iterator it = map.find(key); if(it != map.end()) { return it->second; } return 0; } template bool is_component_cached(const C& map, const std::string& key) const { return (map.find(key) != map.end()); } template bool is_component_cached(const C& map, const std::string& key, const typename C::mapped_type value) const { auto range = map.equal_range(key); for (auto& it = range.first; it != range.second; ++it) { if(it->first == key && it->second == value) { return true; } } return false; } void cache_pod(container_pod_map& map, const std::string& id, const k8s_pod_t* pod); template void cache_component(C& map, const std::string& key, typename C::mapped_type component) { ASSERT(component); ASSERT(!component->get_name().empty()); map.insert(typename C::value_type(key, component)); return; } template void uncache_component(C& map, const std::string& key) { typename C::iterator it = map.find(key); if(it != map.end()) { map.erase(it); } } #ifndef HAS_ANALYZER namespace_map& get_namespace_map() { return m_namespace_map; } container_pod_map& get_container_pod_map() { return m_container_pods; } pod_service_map& get_pod_service_map() { return m_pod_services; } pod_rc_map& get_pod_rc_map() { return m_pod_rcs; } pod_rs_map& get_pod_rs_map() { return m_pod_rss; } pod_deployment_map& get_pod_deployment_map() { return m_pod_deployments; } #endif // HAS_ANALYZER static const std::string m_docker_prefix; // "docker://" static const std::string m_rkt_prefix; // "rkt://" static const std::string m_containerd_prefix; // "containerd://" static const std::string m_crio_prefix; // "cri-o://" static const unsigned m_id_length; // portion of the ID to be cached (=12) #ifndef HAS_ANALYZER namespace_map m_namespace_map; container_pod_map m_container_pods; pod_service_map m_pod_services; pod_rc_map m_pod_rcs; pod_rs_map m_pod_rss; pod_deployment_map m_pod_deployments; #endif // HAS_ANALYZER #ifdef HAS_CAPTURE event_list_t m_capture_events; #endif // HAS_CAPTURE typedef std::unordered_map component_map_t; k8s_namespaces m_namespaces; k8s_nodes m_nodes; k8s_pods m_pods; k8s_controllers m_controllers; k8s_replicasets m_replicasets; k8s_services m_services; k8s_daemonsets m_daemonsets; k8s_deployments m_deployments; k8s_events m_events; // map for uid/type cache for all components // used by to quickly lookup any component by uid component_map_t m_component_map; bool m_is_captured; int m_capture_version = -1; friend class k8s_dispatcher; friend class k8s_handler; friend class k8s; }; // namespaces inline const k8s_namespaces& k8s_state_t::get_namespaces() const { return m_namespaces; } inline k8s_namespaces& k8s_state_t::get_namespaces() { return m_namespaces; } inline void k8s_state_t::push_namespace(const k8s_ns_t& ns) { m_namespaces.push_back(ns); } inline void k8s_state_t::emplace_namespace(k8s_ns_t&& ns) { m_namespaces.emplace_back(std::move(ns)); } // nodes inline const k8s_nodes& k8s_state_t::get_nodes() const { return m_nodes; } inline k8s_nodes& k8s_state_t::get_nodes() { return m_nodes; } inline void k8s_state_t::push_node(const k8s_node_t& node) { m_nodes.push_back(node); } inline void k8s_state_t::emplace_node(k8s_node_t&& node) { m_nodes.emplace_back(std::move(node)); } // pods inline const k8s_pods& k8s_state_t::get_pods() const { return m_pods; } inline k8s_pods& k8s_state_t::get_pods() { return m_pods; } inline void k8s_state_t::push_pod(const k8s_pod_t& pod) { m_pods.push_back(pod); } inline void k8s_state_t::emplace_pod(k8s_pod_t&& pod) { m_pods.emplace_back(std::move(pod)); } inline const k8s_pod_t::container_id_list& k8s_state_t::get_pod_container_ids(k8s_pod_t& pod) { return pod.get_container_ids(); } // replication controllers inline const k8s_controllers& k8s_state_t::get_rcs() const { return m_controllers; } inline k8s_controllers& k8s_state_t::get_rcs() { return m_controllers; } inline void k8s_state_t::push_rc(const k8s_rc_t& rc) { m_controllers.push_back(rc); } inline void k8s_state_t::emplace_rc(k8s_rc_t&& rc) { m_controllers.emplace_back(std::move(rc)); } // replica sets inline const k8s_replicasets& k8s_state_t::get_rss() const { return m_replicasets; } inline k8s_replicasets& k8s_state_t::get_rss() { return m_replicasets; } inline void k8s_state_t::push_rs(const k8s_rs_t& rs) { m_replicasets.push_back(rs); } inline void k8s_state_t::emplace_rs(k8s_rs_t&& rs) { m_replicasets.emplace_back(std::move(rs)); } // services inline const k8s_services& k8s_state_t::get_services() const { return m_services; } inline k8s_services& k8s_state_t::get_services() { return m_services; } inline void k8s_state_t::push_service(const k8s_service_t& service) { m_services.push_back(service); } inline void k8s_state_t::emplace_service(k8s_service_t&& service) { m_services.emplace_back(std::move(service)); } // daemonsets inline const k8s_daemonsets& k8s_state_t::get_daemonsets() const { return m_daemonsets; } inline k8s_daemonsets& k8s_state_t::get_daemonsets() { return m_daemonsets; } inline void k8s_state_t::push_daemonset(const k8s_daemonset_t& daemonset) { m_daemonsets.push_back(daemonset); } inline void k8s_state_t::emplace_daemonset(k8s_daemonset_t&& daemonset) { m_daemonsets.emplace_back(std::move(daemonset)); } // deployments inline const k8s_deployments& k8s_state_t::get_deployments() const { return m_deployments; } inline k8s_deployments& k8s_state_t::get_deployments() { return m_deployments; } inline void k8s_state_t::push_deployment(const k8s_deployment_t& deployment) { m_deployments.push_back(deployment); } inline void k8s_state_t::emplace_deployment(k8s_deployment_t&& deployment) { m_deployments.emplace_back(std::move(deployment)); } // events inline const k8s_events& k8s_state_t::get_events() const { return m_events; } inline k8s_events& k8s_state_t::get_events() { return m_events; } inline void k8s_state_t::clear_events() { for(auto it = m_events.begin(); it != m_events.end();) { it->post_process((*this)); if(!it->has_pending_events()) { it = m_events.erase(it); } else { ++it; } } } inline void k8s_state_t::push_event(const k8s_event_t& evt) { m_events.push_back(evt); } inline void k8s_state_t::emplace_event(k8s_event_t&& evt) { m_events.emplace_back(std::move(evt)); } // general inline void k8s_state_t::set_last_pod_node_name(const std::string& name) { if(m_pods.size()) { m_pods.back().set_node_name(name); } } inline void k8s_state_t::set_last_pod_host_ip(const std::string& host_ip) { if(m_pods.size()) { m_pods.back().set_host_ip(host_ip); } } inline void k8s_state_t::set_last_pod_internal_ip(const std::string& internal_ip) { if(m_pods.size()) { m_pods.back().set_internal_ip(internal_ip); } } inline void k8s_state_t::add_last_node_ip(std::string&& ip) { if(m_nodes.size()) { m_nodes.back().emplace_host_ip(std::move(ip)); } } inline void k8s_state_t::add_last_pod_container_id(std::string&& container_id) { if(m_pods.size()) { m_pods.back().emplace_container_id(std::move(container_id)); } } inline void k8s_state_t::set_capture_version(int version) { if(version != CAPTURE_VERSION_NONE && version != CAPTURE_VERSION_1 && version != CAPTURE_VERSION_2) { throw sinsp_exception(std::string("K8s invalid capture version (") + std::to_string(version) + ')'); } m_capture_version = version; } inline int k8s_state_t::get_capture_version() const { return m_capture_version; } sysdig-0.26.4/userspace/libsinsp/logger.cpp000066400000000000000000000132321352731327100207120ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Sysdig, Inc. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "logger.h" #include "sinsp.h" #include "sinsp_int.h" #ifndef _WIN32 #include #else #include #endif #include namespace { thread_local char s_tbuf[16384]; const size_t ENCODE_LEN = sizeof(uint64_t); } // end namespace const uint32_t sinsp_logger::OT_NONE = 0; const uint32_t sinsp_logger::OT_STDOUT = 1; const uint32_t sinsp_logger::OT_STDERR = (OT_STDOUT << 1); const uint32_t sinsp_logger::OT_FILE = (OT_STDERR << 1); const uint32_t sinsp_logger::OT_CALLBACK = (OT_FILE << 1); const uint32_t sinsp_logger::OT_NOTS = (OT_CALLBACK << 1); const uint32_t sinsp_logger::OT_ENCODE_SEV = (OT_NOTS << 1); sinsp_logger::sinsp_logger(): m_file(nullptr), m_callback(nullptr), m_flags(OT_NONE), m_sev(SEV_INFO) { } sinsp_logger::~sinsp_logger() { if(m_file) { ASSERT(m_flags & sinsp_logger::OT_FILE); fclose(m_file); } } bool sinsp_logger::is_callback() const { return (m_flags & sinsp_logger::OT_CALLBACK) != 0; } uint32_t sinsp_logger::get_log_output_type() const { return m_flags; } void sinsp_logger::add_stdout_log() { m_flags |= sinsp_logger::OT_STDOUT; } void sinsp_logger::add_stderr_log() { m_flags |= sinsp_logger::OT_STDERR; } void sinsp_logger::add_file_log(const std::string& filename) { ASSERT(m_file == nullptr); m_file = fopen(filename.c_str(), "w"); if(!m_file) { throw sinsp_exception("Unable to open file " + filename + " for writing"); } m_flags |= sinsp_logger::OT_FILE; } void sinsp_logger::disable_timestamps() { m_flags |= sinsp_logger::OT_NOTS; } void sinsp_logger::add_encoded_severity() { m_flags |= sinsp_logger::OT_ENCODE_SEV; } void sinsp_logger::add_callback_log(const sinsp_logger_callback callback) { const sinsp_logger_callback old_cb = m_callback.exchange(callback); ASSERT(old_cb == nullptr); // For release builds, the compiler doesn't see that old_cb is used, // so do something that will satisfy the compiler static_cast(old_cb); m_flags |= sinsp_logger::OT_CALLBACK; } void sinsp_logger::remove_callback_log() { m_callback = nullptr; m_flags &= ~sinsp_logger::OT_CALLBACK; } void sinsp_logger::set_severity(const severity sev) { if(m_sev < SEV_MIN || m_sev > SEV_MAX) { throw sinsp_exception("Invalid log severity"); } m_sev = sev; } sinsp_logger::severity sinsp_logger::get_severity() const { return m_sev; } void sinsp_logger::log(std::string msg, const severity sev) { sinsp_logger_callback cb = nullptr; if(sev > m_sev) { return; } if((m_flags & sinsp_logger::OT_NOTS) == 0) { struct timeval ts = {}; if(gettimeofday(&ts, nullptr) == 0) { const std::string::size_type ts_length = sizeof("31-12 23:59:59.999999 "); char ts_buf[ts_length]; struct tm time_info = {}; gmtime_r(&ts.tv_sec, &time_info); snprintf(ts_buf, sizeof(ts_buf), "%.2d-%.2d %.2d:%.2d:%.2d.%.6d ", time_info.tm_mon + 1, time_info.tm_mday, time_info.tm_hour, time_info.tm_min, time_info.tm_sec, (int)ts.tv_usec); ts_buf[sizeof(ts_buf) - 1] = '\0'; msg.insert(0, ts_buf); } } if(m_flags & sinsp_logger::OT_ENCODE_SEV) { char sev_buf[ENCODE_LEN + 1]; strncpy(sev_buf, encode_severity(sev), sizeof(sev_buf)); sev_buf[sizeof(sev_buf) - 1] = 0; msg.insert(0, sev_buf); } if(is_callback()) { cb = m_callback; } if(cb != nullptr) { cb(std::move(msg), sev); } else if((m_flags & sinsp_logger::OT_FILE) && m_file) { fprintf(m_file, "%s\n", msg.c_str()); fflush(m_file); } else if(m_flags & sinsp_logger::OT_STDOUT) { fprintf(stdout, "%s\n", msg.c_str()); fflush(stdout); } else if(m_flags & sinsp_logger::OT_STDERR) { fprintf(stderr, "%s\n", msg.c_str()); fflush(stderr); } } const char* sinsp_logger::format(const severity sev, const char* const fmt, ...) { va_list ap; va_start(ap, fmt); vsnprintf(s_tbuf, sizeof s_tbuf, fmt, ap); va_end(ap); log(s_tbuf, sev); return s_tbuf; } const char* sinsp_logger::format(const char* const fmt, ...) { va_list ap; va_start(ap, fmt); vsnprintf(s_tbuf, sizeof s_tbuf, fmt, ap); va_end(ap); log(s_tbuf, SEV_INFO); return s_tbuf; } namespace { // All severity strings should be ENCODE_LEN chars long const char* SEV_LEVELS[] = { "SEV_DEF ", "SEV_FAT ", "SEV_CRI ", "SEV_ERR ", "SEV_WAR ", "SEV_NOT ", "SEV_INF ", "SEV_DEB ", "SEV_TRA " }; static_assert(sizeof(SEV_LEVELS) == sizeof(*SEV_LEVELS) * ((size_t)(sinsp_logger::SEV_MAX) + 1), "severity array must have SEV_MAX+1 elements"); } const char* sinsp_logger::encode_severity(const sinsp_logger::severity sev) { const char* ret; auto sev_int = (size_t)sev; if (sev_int > SEV_MAX) { sev_int = 0; } ret = SEV_LEVELS[sev_int]; assert(strlen(ret) == ENCODE_LEN); return ret; } size_t sinsp_logger::decode_severity(const std::string &str, severity& sev) { if(str.length() < ENCODE_LEN) { return 0; } const char* msg = str.c_str(); // we don't really expect "SEV_DEF " messages so skip severity 0 for(size_t i = SEV_MIN; i <= SEV_MAX; ++i) { if(!strncmp(msg, SEV_LEVELS[i], ENCODE_LEN)) { sev = static_cast(i); return ENCODE_LEN; } } return 0; }sysdig-0.26.4/userspace/libsinsp/logger.h000066400000000000000000000211441352731327100203600ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Sysdig, Inc. This file is part of sysdig. 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. */ #pragma once #include "sinsp_public.h" #include #include /** * Sysdig component logging API. This API exposes the ability to log to a * variety of log sinks. sinsp_logger will use only one enabled log* sink; * if multiple are enabled, then it will use the first available one it * finds. The order in which log sinks is considered is: (1) a registered * callback function, (2) a registered file, (3) standard output, and * (4) standard error. */ class SINSP_PUBLIC sinsp_logger { public: enum severity { SEV_FATAL = 1, SEV_CRITICAL = 2, SEV_ERROR = 3, SEV_WARNING = 4, SEV_NOTICE = 5, SEV_INFO = 6, SEV_DEBUG = 7, SEV_TRACE = 8, }; const static severity SEV_MIN = SEV_FATAL; const static severity SEV_MAX = SEV_TRACE; using callback_t = void (*)(std::string&& str, severity sev); const static uint32_t OT_NONE; const static uint32_t OT_STDOUT; const static uint32_t OT_STDERR; const static uint32_t OT_FILE; const static uint32_t OT_CALLBACK; const static uint32_t OT_NOTS; const static uint32_t OT_ENCODE_SEV; /** * Initialize this sinsp_logger with no output sinks enabled. */ sinsp_logger(); ~sinsp_logger(); /** * Get the currently configured output type, which includes the * configured output sinks as well as whether timestamps are enabled * or not. */ uint32_t get_log_output_type() const; /** Enable the standard output log sink. */ void add_stdout_log(); /** Enable the standard error log sink. */ void add_stderr_log(); /** * Enable the file log sink. * * @param[in] filename The filename to which sinsp_logger should write * logs. */ void add_file_log(const std::string& filename); /** Disables tagging logs with the current timestamp. */ void disable_timestamps(); /** Adds encoded severity to log messages */ void add_encoded_severity(); /** * Registered the given callback as the logging callback. * * Note: the given callback must be thread-safe. */ void add_callback_log(callback_t callback); /** Deregister any registered logging callbacks. */ void remove_callback_log(); /** * Set the minimum severity of logs that this sinsp_logger will emit. */ void set_severity(severity sev); /** * Returns the minimum severity of logs that this sinsp_logger * will emit. */ severity get_severity() const; /** * Returns true if logs generated at the given severity will be written * to the logging sink, false otherwise. * * Note that this is intentionally inline. */ bool is_enabled(const severity sev) const { return (sev <= m_sev); } /** * Emit the given msg to the configured log sink if the given sev * is greater than or equal to the minimum configured logging severity. */ void log(std::string msg, severity sev = SEV_INFO); /** * Write the given printf-style log message of the given severity * with the given format to the configured log sink. * * @returns a pointer to static thread-local storage containing the * formatted log message. */ const char* format(severity sev, const char* fmt, ...); /** * Write the given printf-style log message of SEV_INFO severity * with the given format to the configured log sink. * * @returns a pointer to static thread-local storage containing the * formatted log message. */ const char* format(const char* fmt, ...); /** Sets `sev` to the decoded severity or SEV_MAX+1 for errors. * Returns the length of the severity string on success * and 0 in case of errors */ static size_t decode_severity(const std::string &s, severity& sev); private: /** Returns true if the callback log sync is enabled, false otherwise. */ bool is_callback() const; /** Returns a string containing encoded severity, for OT_ENCODE_SEV. */ static const char* encode_severity(severity sev); std::atomic m_file; std::atomic m_callback; std::atomic m_flags; std::atomic m_sev; }; using sinsp_logger_callback = sinsp_logger::callback_t; extern sinsp_logger g_logger; #define SINSP_LOG_(severity, fmt, ...) \ do \ { \ if(g_logger.is_enabled(severity)) \ { \ g_logger.format((severity), ("" fmt), ##__VA_ARGS__); \ } \ } \ while(false) #define SINSP_LOG_STR_(severity, msg) \ do \ { \ if(g_logger.is_enabled(severity)) \ { \ g_logger.log((msg), (severity)); \ } \ } \ while(false) #define SINSP_FATAL(...) SINSP_LOG_(sinsp_logger::SEV_FATAL, ##__VA_ARGS__) #define SINSP_CRITICAL(...) SINSP_LOG_(sinsp_logger::SEV_CRITICAL, ##__VA_ARGS__) #define SINSP_ERROR(...) SINSP_LOG_(sinsp_logger::SEV_ERROR, ##__VA_ARGS__) #define SINSP_WARNING(...) SINSP_LOG_(sinsp_logger::SEV_WARNING, ##__VA_ARGS__) #define SINSP_NOTICE(...) SINSP_LOG_(sinsp_logger::SEV_NOTICE, ##__VA_ARGS__) #define SINSP_INFO(...) SINSP_LOG_(sinsp_logger::SEV_INFO, ##__VA_ARGS__) #define SINSP_DEBUG(...) SINSP_LOG_(sinsp_logger::SEV_DEBUG, ##__VA_ARGS__) #define SINSP_TRACE(...) SINSP_LOG_(sinsp_logger::SEV_TRACE, ##__VA_ARGS__) #define SINSP_STR_FATAL(str) SINSP_LOG_STR_(sinsp_logger::SEV_FATAL, (str)) #define SINSP_STR_CRITICAL(str) SINSP_LOG_STR_(sinsp_logger::SEV_CRITICAL,(str)) #define SINSP_STR_ERROR(str) SINSP_LOG_STR_(sinsp_logger::SEV_ERROR, (str)) #define SINSP_STR_WARNING(str) SINSP_LOG_STR_(sinsp_logger::SEV_WARNING, (str)) #define SINSP_STR_NOTICE(str) SINSP_LOG_STR_(sinsp_logger::SEV_NOTICE, (str)) #define SINSP_STR_INFO(str) SINSP_LOG_STR_(sinsp_logger::SEV_INFO, (str)) #define SINSP_STR_DEBUG(str) SINSP_LOG_STR_(sinsp_logger::SEV_DEBUG, (str)) #define SINSP_STR_TRACE(str) SINSP_LOG_STR_(sinsp_logger::SEV_TRACE, (str)) #if _DEBUG # define DBG_SINSP_FATAL(...) SINSP_FATAL( __VA_ARGS__) # define DBG_SINSP_CRITICAL(...) SINSP_CRITICAL(__VA_ARGS__) # define DBG_SINSP_ERROR(...) SINSP_ERROR( __VA_ARGS__) # define DBG_SINSP_WARNING(...) SINSP_WARNING( __VA_ARGS__) # define DBG_SINSP_NOTICE(...) SINSP_NOTICE( __VA_ARGS__) # define DBG_SINSP_INFO(...) SINSP_INFO( __VA_ARGS__) # define DBG_SINSP_DEBUG(...) SINSP_DEBUG( __VA_ARGS__) # define DBG_SINSP_TRACE(...) SINSP_TRACE( __VA_ARGS__) # define DBG_SINSP_STR_FATAL(str) SINSP_STR_FATAL(str) # define DBG_SINSP_STR_CRITICAL(str) SINSP_STR_CRITICAL(str) # define DBG_SINSP_STR_ERROR(str) SINSP_STR_ERROR(str) # define DBG_SINSP_STR_WARNING(str) SINSP_STR_WARNING(str) # define DBG_SINSP_STR_NOTICE(str) SINSP_STR_NOTICE(str) # define DBG_SINSP_STR_INFO(str) SINSP_STR_INFO(str) # define DBG_SINSP_STR_DEBUG(str) SINSP_STR_DEBUG(str) # define DBG_SINSP_STR_TRACE(str) SINSP_STR_TRACE(str) #else # define DBG_SINSP_FATAL(fmt, ...) # define DBG_SINSP_CRITICAL(fmt, ...) # define DBG_SINSP_ERROR(fmt, ...) # define DBG_SINSP_WARNING(fmt, ...) # define DBG_SINSP_NOTICE(fmt, ...) # define DBG_SINSP_INFO(fmt, ...) # define DBG_SINSP_DEBUG(fmt, ...) # define DBG_SINSP_TRACE(fmt, ...) # define DBG_SINSP_STR_FATAL(str) # define DBG_SINSP_STR_CRITICAL(str) # define DBG_SINSP_STR_ERROR(str) # define DBG_SINSP_STR_WARNING(str) # define DBG_SINSP_STR_NOTICE(str) # define DBG_SINSP_STR_INFO(str) # define DBG_SINSP_STR_DEBUG(str) # define DBG_SINSP_STR_TRACE(str) #endif sysdig-0.26.4/userspace/libsinsp/lua_parser.cpp000066400000000000000000000035241352731327100215730ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include "sinsp.h" #include "filter.h" #include "sinsp_int.h" #include "lua_parser.h" #include "lua_parser_api.h" extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" } const static struct luaL_reg ll_filter [] = { {"rel_expr", &lua_parser_cbacks::rel_expr}, {"bool_op", &lua_parser_cbacks::bool_op}, {"nest", &lua_parser_cbacks::nest}, {"unnest", &lua_parser_cbacks::unnest}, {NULL,NULL} }; lua_parser::lua_parser(gen_event_filter_factory &factory, lua_State *ls, const char *lua_library_name) : m_factory(factory) { m_filter = NULL; m_ls = ls; reset(); // Register our c++ defined functions luaL_openlib(m_ls, lua_library_name, ll_filter, 0); } void lua_parser::reset() { m_have_rel_expr = false; m_last_boolop = BO_NONE; m_nest_level = 0; m_filter = m_factory.new_filter(); } gen_event_filter* lua_parser::get_filter(bool reset_filter) { if (m_nest_level != 0) { throw sinsp_exception("Error in configured filter: unbalanced nesting"); } gen_event_filter *ret = m_filter; if (reset_filter) { reset(); } return ret; } lua_parser::~lua_parser() { // The lua state is not considered owned by this object, so // not freeing it. delete m_filter; m_filter = NULL; } sysdig-0.26.4/userspace/libsinsp/lua_parser.h000066400000000000000000000021611352731327100212340ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include "lua_parser_api.h" #include "gen_filter.h" typedef struct lua_State lua_State; class lua_parser { public: lua_parser(gen_event_filter_factory &factory, lua_State *ls, const char *lua_library_name); virtual ~lua_parser(); gen_event_filter* get_filter(bool reset_filter = false); private: void reset(); gen_event_filter_factory &m_factory; gen_event_filter* m_filter; boolop m_last_boolop; bool m_have_rel_expr; int32_t m_nest_level; lua_State* m_ls; friend class lua_parser_cbacks; }; sysdig-0.26.4/userspace/libsinsp/lua_parser_api.cpp000066400000000000000000000136251352731327100224270ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "sinsp.h" #include "sinsp_int.h" #include "filterchecks.h" #include "lua_parser_api.h" #include "lua_parser.h" extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" } extern sinsp_filter_check_list g_filterlist; // It would be nice to expose this up to Lua so that comparison operator // parsing/encoding can be done there. static cmpop string_to_cmpop(const char* str) { if(strcmp(str, "=") == 0) { return CO_EQ; } else if(strcmp(str, "!=") == 0) { return CO_NE; } else if(strcmp(str, "<=") == 0) { return CO_LE; } else if(strcmp(str, "<") == 0) { return CO_LT; } else if(strcmp(str, ">=") == 0) { return CO_GE; } else if(strcmp(str, ">") == 0) { return CO_GT; } else if(strcmp(str, "contains") == 0) { return CO_CONTAINS; } else if(strcmp(str, "icontains") == 0) { return CO_ICONTAINS; } else if(strcmp(str, "startswith") == 0) { return CO_STARTSWITH; } else if(strcmp(str, "endswith") == 0) { return CO_ENDSWITH; } else if(strcmp(str, "in") == 0) { return CO_IN; } else if(strcmp(str, "pmatch") == 0) { return CO_PMATCH; } else if(strcmp(str, "exists") == 0) { return CO_EXISTS; } else if(strcmp(str, "glob") == 0) { return CO_GLOB; } else { throw sinsp_exception("filter error: invalid comparison operator: " + string(str)); } } boolop string_to_boolop(const char* str) { if(strcmp(str, "or") == 0) { return BO_OR; } else if(strcmp(str, "and") == 0) { return BO_AND; } else if(strcmp(str, "not") == 0) { return BO_NOT; } else { throw sinsp_exception("filter error: invalid boolean operator: " + string(str)); } } int lua_parser_cbacks::nest(lua_State *ls) { lua_parser* parser = (lua_parser*)lua_topointer(ls, -1); try { if (parser->m_have_rel_expr && parser->m_last_boolop == BO_NONE) { string err = "filter.nest() called without a preceding call to filter.bool_op()"; throw sinsp_exception(err); } gen_event_filter* filter = parser->m_filter; filter->push_expression(parser->m_last_boolop); parser->m_nest_level++; parser->m_last_boolop = BO_NONE; parser->m_have_rel_expr = false; } catch (exception &e) { lua_pushstring(ls, e.what()); lua_error(ls); } return 0; } int lua_parser_cbacks::unnest(lua_State *ls) { lua_parser* parser = (lua_parser*)lua_topointer(ls, -1); try { if (parser->m_nest_level < 1) { string err = "filter.unnest() called without being nested"; throw sinsp_exception(err); } gen_event_filter* filter = parser->m_filter; filter->pop_expression(); parser->m_nest_level--; } catch (exception &e) { lua_pushstring(ls, e.what()); lua_error(ls); } return 0; } int lua_parser_cbacks::bool_op(lua_State *ls) { lua_parser* parser = (lua_parser*)lua_topointer(ls, -2); try { const char* opstr = luaL_checkstring(ls, -1); boolop op = string_to_boolop(opstr); if (!parser->m_have_rel_expr) { if (op == BO_NOT) { op = (boolop)((uint32_t)parser->m_last_boolop | op); } else { string err = "filter.bool_op() called without having called rel_expr() "; throw sinsp_exception(err); } } if (parser->m_last_boolop != BO_NONE) { if (op == BO_NOT) { op = (boolop)((uint32_t)parser->m_last_boolop | op); } else { string err = "filter.bool_op() called twice in a row"; throw sinsp_exception(err); } } parser->m_last_boolop = op; } catch (exception &e) { lua_pushstring(ls, e.what()); lua_error(ls); } return 0; } int lua_parser_cbacks::rel_expr(lua_State *ls) { lua_parser* parser = (lua_parser*)lua_topointer(ls, 1); try { if (parser->m_have_rel_expr && parser->m_last_boolop == BO_NONE) { string err = "filter.rel_expr() called twice in a row"; throw sinsp_exception(err); } parser->m_have_rel_expr = true; gen_event_filter* filter = parser->m_filter; const char* fld = luaL_checkstring(ls, 2); gen_event_filter_check *chk = parser->m_factory.new_filtercheck(fld); if(chk == NULL) { string err = "filter_check called with nonexistent field " + string(fld); throw sinsp_exception("parser API error"); } int i; int rule_index = 0; chk->m_boolop = parser->m_last_boolop; parser->m_last_boolop = BO_NONE; chk->parse_field_name(fld, true, true); const char* cmpop = luaL_checkstring(ls, 3); chk->m_cmpop = string_to_cmpop(cmpop); // "exists" is the only unary comparison op if(strcmp(cmpop, "exists")) { if (strcmp(cmpop, "in") == 0 || strcmp(cmpop, "pmatch") == 0) { if (!lua_istable(ls, 4)) { string err = "Got non-table as in-expression operand\n"; throw sinsp_exception("parser API error"); } int n = luaL_getn(ls, 4); /* get size of table */ for (i=1; i<=n; i++) { lua_rawgeti(ls, 4, i); const char* value = luaL_checkstring(ls, 6); chk->add_filter_value(value, strlen(value), i - 1); lua_pop(ls, 1); } } else { const char* value = luaL_checkstring(ls, 4); chk->add_filter_value(value, strlen(value)); } if (lua_isnumber(ls, 5)) { rule_index = (int) luaL_checkinteger(ls, 5); } } else { if (lua_isnumber(ls, 4)) { rule_index = (int) luaL_checkinteger(ls, 4); } } if (rule_index) { chk->set_check_id(rule_index); } filter->add_check(chk); } catch (exception &e) { lua_pushstring(ls, e.what()); lua_error(ls); } return 0; } sysdig-0.26.4/userspace/libsinsp/lua_parser_api.h000066400000000000000000000040101352731327100220600ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once typedef struct lua_State lua_State; class lua_parser_filtercheck { public: lua_parser_filtercheck() {}; virtual ~lua_parser_filtercheck() {}; boolop m_boolop; cmpop m_cmpop; virtual int32_t parse_field_name(const char* str, bool alloc_state, bool needed_for_filtering) = 0; virtual void add_filter_value(const char* str, uint32_t len, uint32_t i = 0 ) = 0; virtual void set_check_id(int32_t id) = 0; }; class lua_parser_filter { public: lua_parser_filter() {}; virtual ~lua_parser_filter() {}; virtual void push_expression(boolop op) = 0; virtual void pop_expression() = 0; virtual void add_check(lua_parser_filtercheck* chk) = 0; }; class lua_filter_factory { public: lua_filter_factory() {}; virtual ~lua_filter_factory() {}; // Create a new filter virtual lua_parser_filter *new_filter() = 0; // Create a new filterchekc virtual lua_parser_filtercheck *new_filtercheck(const char *fldname) = 0; }; class lua_parser_cbacks { public: // filter.rel_expr(field_name, cmpop, value, index) // field_name and cmpop are mandatory // value is mandatory unless cmpop=="exists" // index is an optional index (integer) that will be written // into events matching this expression (internal use). static int rel_expr(lua_State *ls); // filter.bool_op(op) static int bool_op(lua_State *ls); // filter.nest() static int nest(lua_State *ls); // filter.unnest() static int unnest(lua_State *ls); }; sysdig-0.26.4/userspace/libsinsp/marathon_component.cpp000066400000000000000000000166341352731327100233370ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // marathon_component.cpp // #include "marathon_component.h" #include "sinsp.h" #include "sinsp_int.h" #include #include // // component // marathon_component::task_app_map_t marathon_component::m_task_app_cache; const marathon_component::component_map marathon_component::list = { { marathon_component::MARATHON_GROUP, "group" }, { marathon_component::MARATHON_APP, "app" } }; marathon_component::marathon_component(type t, const std::string& id) : m_type(t), m_id(id) { component_map::const_iterator it = list.find(t); if(it == list.end()) { throw sinsp_exception("Invalid Marathon component type: " + std::to_string(t)); } if(m_id.empty()) { throw sinsp_exception("Marathon " + it->second + " ID cannot be empty"); } } marathon_component::marathon_component(const marathon_component& other): m_type(other.m_type), m_id(other.m_id) { } marathon_component::marathon_component(marathon_component&& other): m_type(other.m_type), m_id(std::move(other.m_id)) { } marathon_component& marathon_component::operator=(const marathon_component& other) { m_type = other.m_type; m_id = other.m_id; return *this; } marathon_component& marathon_component::operator=(const marathon_component&& other) { m_type = other.m_type; m_id = std::move(other.m_id); return *this; } std::string marathon_component::get_type_name(type t) { component_map::const_iterator it = list.find(t); if(it != list.end()) { return it->second; } std::ostringstream os; os << "Unknown component type " << static_cast(t); throw sinsp_exception(os.str().c_str()); } marathon_component::type marathon_component::get_type(const std::string& name) { if(name == "group") { return MARATHON_GROUP; } else if(name == "app") { return MARATHON_APP; } std::ostringstream os; os << "Unknown component name " << name; throw sinsp_exception(os.str().c_str()); } void marathon_component::cache_task_app(const std::string& task_id, app_ptr_t app) { m_task_app_cache[task_id] = app; } void marathon_component::uncache_task_app(const std::string& task_id) { m_task_app_cache.erase(task_id); } marathon_component::app_ptr_t marathon_component::get_cached_app(const std::string& task_id) { task_app_map_t::iterator it = m_task_app_cache.find(task_id); if(it != m_task_app_cache.end()) { return it->second; } return 0; } // // app // marathon_app::marathon_app(const std::string& id) : marathon_component(marathon_component::MARATHON_APP, id) { } marathon_app::~marathon_app() { } void marathon_app::add_task(mesos_framework::task_ptr_t ptask) { if(ptask) { const std::string& task_id = ptask->get_uid(); ptask->set_marathon_app_id(get_id()); for(auto& task : m_tasks) { if(task == task_id) { return; } } m_tasks.push_back(task_id); cache_task_app(task_id, shared_from_this()); } else { g_logger.log("Attempt to add null task to app [" + get_id() + ']', sinsp_logger::SEV_WARNING); } } bool marathon_app::remove_task(const std::string& task_id) { for(auto it = m_tasks.begin(); it != m_tasks.end(); ++it) { if(task_id == *it) { m_tasks.erase(it); uncache_task_app(task_id); return true; } } g_logger.log("Task [" + task_id + "] not found in app [" + get_id() + ']', sinsp_logger::SEV_WARNING); return false; } bool marathon_app::has_task(const std::string& task_id) { for(auto it = m_tasks.begin(); it != m_tasks.end(); ++it) { if(task_id == *it) { return true; } } return false; } std::string marathon_app::get_group_id() const { return get_group_id(get_id()); } std::string marathon_app::get_group_id(const std::string& app_id) { std::string group_id; std::string::size_type pos = app_id.rfind('/'); if(pos != std::string::npos && app_id.length() > pos) { pos += (pos == 0) ? 1 : 0; group_id = app_id.substr(0, pos); } return group_id; } void marathon_app::set_labels(const Json::Value& labels) { m_labels.clear(); Json::Value::Members members = labels.getMemberNames(); for(const auto& member : members) { if(labels[member].isConvertibleTo(Json::ValueType::stringValue)) { m_labels.push_back({member, labels[member].asString()}); } } } mesos_pair_t marathon_app::get_label(const std::string& key) const { for(const auto& label : m_labels) { if(label.first == key) { return label; } } return mesos_pair_t(); } // // group // marathon_group::marathon_group(const std::string& id, const std::string& framework_id) : marathon_component(marathon_component::MARATHON_GROUP, id), m_framework_id(framework_id) { } marathon_group::app_ptr_t marathon_group::get_app(const std::string& id) { for(const auto& app : m_apps) { if(app.second && app.second->get_id() == id) { return app.second; } } return 0; } marathon_group::ptr_t marathon_group::get_group(const std::string& group_id) { if(group_id == get_id()) { return shared_from_this(); } marathon_groups::iterator it = m_groups.find(group_id); if(it != m_groups.end()) { return it->second; } else { for(auto group : m_groups) { if(ptr_t p_group = group.second->get_group(group_id)) { return p_group; } } } return 0; } bool marathon_group::remove(const std::string& id) { if(id == get_id()) { throw sinsp_exception("Invalid access - group can not remove itself."); } if(ptr_t group = get_parent(id)) { return group->remove_group(id); } return false; } marathon_group::ptr_t marathon_group::get_parent(const std::string& id) { marathon_groups::iterator it = m_groups.find(id); if(it != m_groups.end()) { return shared_from_this(); } else { for(auto group : m_groups) { if(group.second->get_group(id)) { return group.second; } } } return 0; } bool marathon_group::remove_group(const std::string& id) { marathon_groups::iterator it = m_groups.find(id); if(it != m_groups.end()) { m_groups.erase(it); return true; } return false; } bool marathon_group::remove_app(const std::string& id) { auto it = m_apps.find(id); if(it != m_apps.end()) { m_apps.erase(it); return true; } return false; } bool marathon_group::remove_task(const std::string& id) { for(auto& app : m_apps) { if(app.second && app.second->remove_task(id)) { return true; } } return false; } marathon_group::app_ptr_t marathon_group::get_app(mesos_task::ptr_t task) const { return get_cached_app(task->get_uid()); } marathon_group::ptr_t marathon_group::get_group(mesos_task::ptr_t task) { for(const auto& app : m_apps) { if(app.second && app.second->has_task(task->get_uid())) { return shared_from_this(); } } for(const auto& group : m_groups) { app_ptr_t app = group.second->get_app(task); if(app) { return group.second; } } return 0; } void marathon_group::print(int indent) const { for(int j = 0; j < indent; ++j) { std::cout << " "; } std::cout << get_id() << std::endl; for(auto& group : m_groups) { group.second->print(indent+2); } } sysdig-0.26.4/userspace/libsinsp/marathon_component.h000066400000000000000000000135571352731327100230050ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // marathon_component.h // // marathon components (groups, apps, tasks) // abstraction // #pragma once #include "json/json.h" #include "sinsp.h" #include "sinsp_int.h" #include "mesos_component.h" #include #include #include #include #include typedef std::pair marathon_pair_t; typedef std::vector marathon_pair_list; class marathon_app; // // component // class marathon_component { public: enum type { MARATHON_GROUP, MARATHON_APP }; typedef std::pair component_pair; typedef std::map component_map; static const component_map list; marathon_component() = delete; marathon_component(type t, const std::string& id); marathon_component(const marathon_component& other); marathon_component(marathon_component&& other); marathon_component& operator=(const marathon_component& other); marathon_component& operator=(const marathon_component&& other); const std::string& get_id() const; void set_id(const std::string& id); const std::string& get_name() const; void set_name(const std::string& name); static std::string get_type_name(type t); static type get_type(const std::string& name); protected: typedef std::shared_ptr app_ptr_t; typedef std::map task_app_map_t; static void cache_task_app(const std::string& task_id, app_ptr_t app); static void uncache_task_app(const std::string& task_id); static app_ptr_t get_cached_app(const std::string& task_id); private: type m_type; std::string m_id; static task_app_map_t m_task_app_cache; }; // // group // class marathon_group : public marathon_component, public std::enable_shared_from_this { public: typedef std::shared_ptr ptr_t; typedef std::shared_ptr app_ptr_t; typedef std::unordered_map> app_map_t; typedef std::vector> app_list_t; typedef std::map> group_map_t; marathon_group(const std::string& id, const std::string& framework_id); app_ptr_t get_app(const std::string& id); app_ptr_t get_app(mesos_task::ptr_t task) const; void add_or_replace_app(std::shared_ptr); bool remove_app(const std::string& id); bool remove_task(const std::string& id); void add_or_replace_group(std::shared_ptr); const app_map_t& get_apps() const; const group_map_t& get_groups() const; ptr_t get_group(const std::string& group_id); ptr_t get_group(mesos_task::ptr_t task); bool remove(const std::string& id); void print(int indent = 0) const; const std::string& get_framework_id() const; void set_framework_id(const std::string& id); private: template static void add_or_replace_component(M& component_map, P comp) { typename M::value_type val = {comp->get_id(), comp}; std::pair ret = component_map.insert(val); if (!ret.second) ret.first->second = comp; } bool remove_group(const std::string& id); ptr_t get_parent(const std::string& id); app_map_t m_apps; group_map_t m_groups; std::string m_framework_id; }; // // app // class marathon_app : public marathon_component, public std::enable_shared_from_this { public: typedef std::shared_ptr ptr_t; typedef std::vector task_list_t; marathon_app(const std::string& uid); ~marathon_app(); void add_task(mesos_framework::task_ptr_t ptask); bool remove_task(const std::string& task_id); const task_list_t& get_tasks() const; bool has_task(const std::string& task_id); std::string get_group_id() const; static std::string get_group_id(const std::string& app_id); void set_labels(const Json::Value& labels); const mesos_pair_list& get_labels() const; mesos_pair_t get_label(const std::string& key) const; private: task_list_t m_tasks; mesos_pair_list m_labels; friend class mesos; }; typedef marathon_group::app_map_t marathon_apps; typedef marathon_group::group_map_t marathon_groups; // // component // inline const std::string& marathon_component::get_id() const { return m_id; } inline void marathon_component::set_id(const std::string& id) { m_id = id; } inline const std::string& marathon_component::get_name() const { return m_id; } inline void marathon_component::set_name(const std::string& name) { m_id = name; } // // group // inline const marathon_group::app_map_t& marathon_group::get_apps() const { return m_apps; } inline const marathon_group::group_map_t& marathon_group::get_groups() const { return m_groups; } inline void marathon_group::add_or_replace_group(std::shared_ptr group) { add_or_replace_component(m_groups, group); } inline void marathon_group::add_or_replace_app(std::shared_ptr app) { add_or_replace_component(m_apps, app); } inline const std::string& marathon_group::get_framework_id() const { return m_framework_id; } inline void marathon_group::set_framework_id(const std::string& id) { m_framework_id = id; } // // app // inline const marathon_app::task_list_t& marathon_app::get_tasks() const { return m_tasks; } inline const mesos_pair_list& marathon_app::get_labels() const { return m_labels; } sysdig-0.26.4/userspace/libsinsp/marathon_http.cpp000066400000000000000000000072221352731327100223050ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // marathon_http.cpp // #ifndef CYGWING_AGENT #ifdef HAS_CAPTURE #include "marathon_http.h" #include "curl/curl.h" #include "curl/easy.h" #define BUFFERSIZE 512 // b64 needs this macro #include "b64/encode.h" #include "sinsp.h" #include "sinsp_int.h" #include "json_error_log.h" #include "mesos.h" #include #include #include #include marathon_http::marathon_http(mesos& m, const uri& url, bool discover_marathon, int timeout_ms, const string& token): mesos_http(m, url, false, discover_marathon, timeout_ms, token) { g_logger.log("Creating Marathon HTTP object for [" + url.to_string(false) + "] ...", sinsp_logger::SEV_DEBUG); if(refresh_data()) { g_logger.log("Created Marathon HTTP connection (" + url.to_string(false) + ") for framework [" + get_framework_name() + "] (" + get_framework_id() + "), version: " + get_framework_version(), sinsp_logger::SEV_INFO); } else { throw sinsp_exception("Could not obtain Mesos Marathon framework information."); } g_logger.log("Marathon request [" + get_request() + ']', sinsp_logger::SEV_DEBUG); } marathon_http::~marathon_http() { } bool marathon_http::refresh_data() { std::ostringstream os; std::string uri = make_uri("/v2/info"); CURLcode res = get_data(uri, os); if(res != CURLE_OK) { std::string errstr = std::string("Problem accessing /v2/info: ") + curl_easy_strerror(res); g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(os.str(), errstr, sinsp_utils::get_current_time_ns(), uri); return false; } try { Json::Value root; Json::Reader reader; if(reader.parse(os.str(), root, false)) { set_framework_id(get_json_string(root, "frameworkId")); set_framework_name(get_json_string(root, "name")); set_framework_version(get_json_string(root, "version")); g_logger.log("Found Marathon framework: " + get_framework_name() + " (" + get_framework_id() + "), version: " + get_framework_version(), sinsp_logger::SEV_DEBUG); } else { std::string errstr; errstr = reader.getFormattedErrorMessages(); g_logger.log("Error parsing framework info (" + errstr + ").\nJSON:\n---\n" + os.str() + "\n---", sinsp_logger::SEV_ERROR); g_json_error_log.log(os.str(), errstr, sinsp_utils::get_current_time_ns(), uri); return false; } } catch(std::exception& ex) { std::string errstr = std::string("Error parsing framework info:") + ex.what(); g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(os.str(), errstr, sinsp_utils::get_current_time_ns(), uri); return false; } return true; } std::string marathon_http::get_groups(const std::string& group_id) { std::ostringstream os; std::string uri = make_uri("/v2/groups" + group_id); CURLcode res = get_data(uri, os); if(res != CURLE_OK) { std::string errstr = std::string("Problem accessing /v2/groups: ") + curl_easy_strerror(res); g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(os.str(), errstr, sinsp_utils::get_current_time_ns(), uri); return ""; } return os.str(); } #endif // HAS_CAPTURE #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/marathon_http.h000066400000000000000000000021411352731327100217450ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // marathon_http.h // #pragma once #ifdef HAS_CAPTURE #include "curl/curl.h" #include "uri.h" #include "mesos_http.h" #include class marathon_http : public mesos_http { public: typedef std::shared_ptr ptr_t; marathon_http(mesos& m, const uri& url, bool discover_marathon, int timeout_ms = 5000L, const string& token = ""); ~marathon_http(); bool refresh_data(); std::string get_groups(const std::string& group_id); private: std::string m_data; }; #endif // HAS_CAPTURE sysdig-0.26.4/userspace/libsinsp/memmem.cpp000066400000000000000000000021571352731327100207140ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #ifndef _GNU_SOURCE #include void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { const unsigned char *ptr; const unsigned char *end; if(needlelen == 0) { return (void *)haystack; } if(haystacklen < needlelen) { return NULL; } end = (const unsigned char *)haystack + haystacklen - needlelen; for(ptr = (const unsigned char *)haystack; ptr <= end; ptr++) { if(!memcmp(ptr, needle, needlelen)) { return (void *)ptr; } } return NULL; } #endif sysdig-0.26.4/userspace/libsinsp/mesos.cpp000066400000000000000000000717431352731327100205740ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // mesos.cpp // #ifndef CYGWING_AGENT #include "mesos.h" #include "mesos_component.h" #include "sinsp.h" #include "sinsp_int.h" #include "json_error_log.h" const mesos_component::component_map mesos::m_components = { { mesos_component::MESOS_FRAMEWORK, "framework" }, { mesos_component::MESOS_TASK, "task" }, { mesos_component::MESOS_SLAVE, "slave" } }; const std::string mesos::default_state_uri = "http://localhost:5050"; const std::string mesos::default_state_api = "/master/state.json"; const std::string mesos::default_marathon_uri = "http://localhost:8080"; const std::string mesos::default_groups_api = "/v2/groups"; const std::string mesos::default_apps_api = "/v2/apps?embed=apps.tasks"; const std::string mesos::default_watch_api = "/v2/events"; const std::string mesos::default_version_api = "/version"; const int mesos::default_timeout_ms = 5000; mesos::mesos(const std::string& mesos_state_json, const std::string& marathon_groups_json, const std::string& marathon_apps_json): m_discover_mesos_leader(false), m_discover_marathon_uris(false), m_testing(true) { if(mesos_state_json.empty() || (marathon_groups_json.empty() && !marathon_apps_json.empty()) || (!marathon_groups_json.empty() && marathon_apps_json.empty())) { throw sinsp_exception("Mesos state AND (both OR none [marathon apps and groups]) are needed"); } mesos_http::json_ptr_t state_json = mesos_http::try_parse(mesos_state_json, "fixed-mesos-state"); if(state_json) { set_state_json(state_json); if(!marathon_groups_json.empty()) { const Json::Value& frameworks = (*state_json)["frameworks"]; if(frameworks.isNull() || !frameworks.isArray()) { throw sinsp_exception("Unexpected condition while detecting Mesos master: frameworks entry not found."); } g_logger.log("Found " + std::to_string(frameworks.size()) + " Mesos frameworks", sinsp_logger::SEV_DEBUG); std::string framework_id; for(auto framework : frameworks) { const Json::Value& name = framework["name"]; if(!name.isNull() && name.isConvertibleTo(Json::stringValue) && mesos_framework::is_root_marathon(name.asString())) { const Json::Value& id = framework["id"]; if(!id.isNull() && id.isConvertibleTo(Json::stringValue)) { framework_id = id.asString(); } } } mesos_http::json_ptr_t dummy_group; set_marathon_groups_json(mesos_http::try_parse(marathon_groups_json, "fixed-marathon-state"), framework_id); set_marathon_apps_json(dummy_group/*mesos_http::try_parse(marathon_apps_json, "fixed-groups-state")*/, framework_id); } collect_data(); } else { throw sinsp_exception("Could not create Mesos state JSON."); } } mesos::mesos(const std::string& state_uri, const uri_list_t& marathon_uris, bool discover_mesos_leader, bool discover_marathon_leader, const credentials_t& mesos_credentials, const credentials_t& marathon_credentials, int timeout_ms, bool is_captured, bool verbose): #ifdef HAS_CAPTURE m_collector(false), m_mesos_uri(state_uri), m_marathon_uris(marathon_uris), #endif // HAS_CAPTURE m_state(is_captured, verbose), m_discover_mesos_leader(discover_mesos_leader), m_discover_marathon_uris(discover_marathon_leader || marathon_uris.empty()), m_timeout_ms(timeout_ms), m_verbose(verbose), m_testing(false) { #ifdef HAS_CAPTURE g_logger.log(std::string("Creating Mesos object for [" + (m_mesos_uri.empty() ? std::string("capture replay") : m_mesos_uri) + "], failover autodiscovery set to ") + (m_discover_mesos_leader ? "true" : "false"), sinsp_logger::SEV_DEBUG); if(m_marathon_uris.size() > 1) { std::string marathon_uri = m_marathon_uris[0]; m_marathon_uris.clear(); m_marathon_uris.emplace_back(marathon_uri); g_logger.log("Multiple root marathon URIs configured; only the first one (" + marathon_uri + ") will have effect;" " others will be treated as generic frameworks (user Marathon frameworks will be discovered).", sinsp_logger::SEV_WARNING); } uri mesos_state_uri(state_uri); mesos_state_uri.get_credentials(m_mesos_credentials); if(m_marathon_uris.size()) { uri marathon_uri(m_marathon_uris[0]); marathon_uri.get_credentials(m_marathon_credentials); } // explicitly specified credentials trump the ones in URI if(!mesos_credentials.first.empty()) { m_mesos_credentials.first = mesos_credentials.first; m_mesos_credentials.second = mesos_credentials.second; } if(!marathon_credentials.first.empty()) { m_marathon_credentials.first = marathon_credentials.first; m_marathon_credentials.second = marathon_credentials.second; } #endif init(); } mesos::mesos(const std::string& state_uri, const uri_list_t& marathon_uris, bool discover_mesos_leader, bool discover_marathon_leader, const credentials_t& dcos_enterprise_credentials, int timeout_ms, bool is_captured, bool verbose): mesos_auth(dcos_enterprise_credentials), #ifdef HAS_CAPTURE m_collector(false), m_mesos_uri(state_uri), m_marathon_uris(marathon_uris), #endif // HAS_CAPTURE m_state(is_captured, verbose), m_discover_mesos_leader(discover_mesos_leader), m_discover_marathon_uris(discover_marathon_leader || marathon_uris.empty()), m_timeout_ms(timeout_ms), m_verbose(verbose), m_testing(false) { #ifdef HAS_CAPTURE g_logger.log(std::string("Creating Mesos object for [" + (m_mesos_uri.empty() ? std::string("capture replay") : m_mesos_uri) + "], failover autodiscovery set to ") + (m_discover_mesos_leader ? "true" : "false"), sinsp_logger::SEV_DEBUG); if(m_marathon_uris.size() > 1) { std::string marathon_uri = m_marathon_uris[0]; m_marathon_uris.clear(); m_marathon_uris.emplace_back(marathon_uri); g_logger.log("Multiple root marathon URIs configured; only the first one (" + marathon_uri + ") will have effect;" " others will be treated as generic frameworks (user Marathon frameworks will be discovered).", sinsp_logger::SEV_WARNING); } #endif init(); } mesos::~mesos() { } void mesos::init() { #ifdef HAS_CAPTURE if(!m_mesos_uri.empty()) { m_collector.remove_all(); if((m_state_http) && (!m_state_http.unique())) { throw sinsp_exception("Invalid access to Mesos initializer: mesos state http client for [" + m_mesos_uri + "] not unique."); } m_state_http = std::make_shared(*this, m_mesos_uri + default_state_api, m_discover_mesos_leader, m_marathon_uris.empty(), m_timeout_ms, m_token); rebuild_mesos_state(true); if(!has_marathon()) { init_marathon(); } } #endif // HAS_CAPTURE } void mesos::init_marathon() { #ifdef HAS_CAPTURE if(!m_mesos_uri.empty()) { m_marathon_groups_http.clear(); m_marathon_apps_http.clear(); const uri_list_t& marathons = m_discover_marathon_uris ? m_state_http->get_marathon_uris() : m_marathon_uris; if(marathons.size()) { g_logger.log("Found " + std::to_string(marathons.size()) + " Marathon URIs", sinsp_logger::SEV_DEBUG); for(const auto& muri : marathons) { g_logger.log("Creating Marathon http objects: " + uri(muri).to_string(false), sinsp_logger::SEV_DEBUG); m_marathon_groups_http[muri] = std::make_shared(*this, muri + default_groups_api, m_discover_marathon_uris, m_timeout_ms, m_token); m_marathon_apps_http[muri] = std::make_shared(*this, muri + default_apps_api, m_discover_marathon_uris, m_timeout_ms, m_token); } if(has_marathon()) { rebuild_marathon_state(true); } } } #endif // HAS_CAPTURE } void mesos::refresh_token() { #ifdef HAS_CAPTURE mesos_auth::refresh_token(); m_state_http->set_token(m_token); if(has_marathon()) { for(auto& group_http : m_marathon_groups_http) { if(group_http.second) { group_http.second->set_token(m_token); } else { throw sinsp_exception("Marathon groups HTTP client is null."); } } for(auto& app_http : m_marathon_apps_http) { if(app_http.second) { app_http.second->set_token(m_token); } else { throw sinsp_exception("Marathon apps HTTP client is null."); } } } #endif // HAS_CAPTURE } #ifdef HAS_CAPTURE const mesos::uri_list_t &mesos::marathon_uris() { return (m_discover_marathon_uris ? m_state_http->get_marathon_uris() : m_marathon_uris); } #endif void mesos::refresh() { rebuild_mesos_state(); if(has_marathon()) { rebuild_marathon_state(); } } void mesos::rebuild_mesos_state(bool full) { #ifdef HAS_CAPTURE if(!m_mesos_uri.empty()) { if(full) { clear_mesos(); if(m_state_http) { m_state_http->get_all_data(&mesos::parse_state); } else { throw sinsp_exception("Mesos state HTTP client is null."); } } else { connect_mesos(); send_mesos_data_request(); collect_data(); } } #endif // HAS_CAPTURE } void mesos::rebuild_marathon_state(bool full) { #ifdef HAS_CAPTURE if(has_marathon()) { if(full) { clear_marathon(); for(auto& group_http : m_marathon_groups_http) { if(group_http.second) { group_http.second->get_all_data(&mesos::parse_groups); } else { throw sinsp_exception("Marathon groups HTTP client is null."); } } for(auto& app_http : m_marathon_apps_http) { if(app_http.second) { app_http.second->get_all_data(&mesos::parse_apps); } else { throw sinsp_exception("Marathon apps HTTP client is null."); } } } else { connect_marathon(); send_marathon_data_request(); collect_data(); } if(m_state_http) { const mesos_http::marathon_uri_t& marathon_uris = m_marathon_uris.empty() ? m_state_http->get_marathon_uris() : m_marathon_uris; if(marathon_uris.size()) { m_state.set_marathon_uri(marathon_uris[0]); } else { throw sinsp_exception("Marathon detected but Marathon URI not found."); } } else { throw sinsp_exception("Mesos state HTTP client is null."); } } #endif // HAS_CAPTURE } #ifdef HAS_CAPTURE void mesos::send_marathon_data_request() { if(has_marathon()) { for(auto& group_http : m_marathon_groups_http) { if(group_http.second) { group_http.second->send_request(); g_logger.log("Marathon groups request sent.", sinsp_logger::SEV_DEBUG); } else { throw sinsp_exception("Marathon groups HTTP client is null."); } } for(auto& app_http : m_marathon_apps_http) { if(app_http.second) { app_http.second->send_request(); g_logger.log("Marathon apps request sent.", sinsp_logger::SEV_DEBUG); } else { throw sinsp_exception("Marathon apps HTTP client is null."); } } } } void mesos::connect_marathon() { if(has_marathon()) { for(auto& group_http : m_marathon_groups_http) { if(!connect(group_http.second, &mesos::set_marathon_groups_json, 2)) { throw sinsp_exception("Connection to Marathon group API failed."); } } for(auto& app_http : m_marathon_apps_http) { if(!connect(app_http.second, &mesos::set_marathon_apps_json, 3)) { throw sinsp_exception("Connection to Marathon app API failed."); } } } } void mesos::send_mesos_data_request() { if(m_state_http) { m_state_http->send_request(); } else { throw sinsp_exception("Mesos state HTTP client is null."); } } void mesos::connect_mesos() { if(!connect(m_state_http, &mesos::set_state_json, 1)) { throw sinsp_exception("Connection to Mesos API failed."); } } #endif // HAS_CAPTURE bool mesos::is_alive() const { #ifdef HAS_CAPTURE if(m_state_http && !m_state_http->is_connected()) { g_logger.log("Mesos state connection loss.", sinsp_logger::SEV_WARNING); return false; } for(const auto& group : m_marathon_groups_http) { if(group.second && !group.second->is_connected()) { g_logger.log("Marathon groups connection loss.", sinsp_logger::SEV_WARNING); return false; } } for(const auto& app : m_marathon_apps_http) { if(app.second && !app.second->is_connected()) { g_logger.log("Marathon apps connection loss.", sinsp_logger::SEV_WARNING); return false; } } #endif // HAS_CAPTURE return true; } #ifdef HAS_CAPTURE void mesos::check_collector_status(int expected) { if(!m_collector.is_healthy(expected)) { throw sinsp_exception("Mesos collector not healthy (has " + std::to_string(m_collector.subscription_count()) + " connections, expected " + std::to_string(expected) + "); giving up on data collection in this cycle ..."); } } void mesos::send_data_request(bool collect) { if(m_mesos_state_json && !m_mesos_state_json->isNull()) { return; } connect_mesos(); send_mesos_data_request(); g_logger.log("Mesos request sent.", sinsp_logger::SEV_DEBUG); if(has_marathon()) { for(auto& group : m_marathon_groups_json) { if(group.second && !group.second->isNull()) { return; } } for(auto& app : m_marathon_apps_json) { if(app.second && !app.second->isNull()) { return; } } connect_marathon(); send_marathon_data_request(); } if(collect) { collect_data(); } } void mesos::capture_frameworks(const Json::Value& root, Json::Value& capture) { const Json::Value& frameworks = root["frameworks"]; if(!frameworks.isNull()) { if(frameworks.isArray()) { if(frameworks.size()) { capture["frameworks"] = Json::arrayValue; for(const auto& framework : frameworks) { Json::Value c_framework; c_framework["active"] = framework["active"]; c_framework["id"] = framework["id"]; c_framework["name"] = framework["name"]; c_framework["hostname"] = framework["hostname"]; c_framework["webui_url"] = framework["webui_url"]; c_framework["tasks"] = Json::arrayValue; Json::Value& c_tasks = c_framework["tasks"]; for(const auto& task : framework["tasks"]) { Json::Value& c_task = c_tasks.append(Json::Value()); c_task["id"] = task["id"]; c_task["name"] = task["name"]; c_task["framework_id"] = task["framework_id"]; c_task["executor_id"] = task["executor_id"]; c_task["slave_id"] = task["slave_id"]; c_task["state"] = task["state"]; //? TODO: statuses c_task["labels"] = task["labels"]; } capture["frameworks"].append(c_framework); } } } } } void mesos::capture_slaves(const Json::Value& root, Json::Value& capture) { const Json::Value& slaves = root["slaves"]; if(!slaves.isNull()) { capture["slaves"] = Json::arrayValue; for(const auto& slave : slaves) { Json::Value c_slave; c_slave["hostname"] = slave["hostname"]; c_slave["id"] = slave["id"]; capture["slaves"].append(c_slave); } } } #endif // HAS_CAPTURE bool mesos::collect_data() { #ifdef HAS_CAPTURE const int tout_s = 30; //TODO: see if we can do better here - instead of timing out, depending on // mesos_collector socket drop detection when remote end closes connection time_t now; time(&now); if(m_last_mesos_refresh && difftime(now, m_last_mesos_refresh) > tout_s) { throw sinsp_exception("Detected stalled Mesos connection (" + std::to_string(difftime(now, m_last_mesos_refresh)) + "s)." " Reconnect attempt in next cycle ..."); } if(m_last_marathon_refresh && difftime(now, m_last_marathon_refresh) > tout_s) { throw sinsp_exception("Detected stalled Marathon connection(" + std::to_string(difftime(now, m_last_marathon_refresh)) + "s)." " Reconnect attempt in next cycle ..."); } if(m_json_error) { throw sinsp_exception("Detected JSON parsing error. Reconnect attempt in next cycle ..."); } bool ret = false; if(m_collector.subscription_count()) { m_collector.get_data(); } if(m_mesos_state_json && !m_mesos_state_json->isNull()) { g_logger.log("Mesos state data detected.", sinsp_logger::SEV_DEBUG); if(has_marathon()) { g_logger.log("Marathon connection detected.", sinsp_logger::SEV_DEBUG); if(!m_marathon_apps_json.empty() && !m_marathon_groups_json.empty()) { g_logger.log("Marathon data detected.", sinsp_logger::SEV_DEBUG); for(auto& group : m_marathon_groups_json) { if(group.second && !group.second->isNull()) { json_map_type_t::iterator app_it = m_marathon_apps_json.find(group.first); if(app_it != m_marathon_apps_json.end()) { if(app_it->second && !app_it->second->isNull()) { parse_state(std::move(*m_mesos_state_json)); m_mesos_state_json.reset(); m_last_mesos_refresh = now; if(m_inactive_frameworks.size()) { g_logger.log("Collection detected " + std::to_string(m_inactive_frameworks.size()) + " inactive frameworks", sinsp_logger::SEV_DEBUG); } if(m_inactive_frameworks.find(group.first) == m_inactive_frameworks.end()) { g_logger.log("Detected active Marathon framework " + group.first, sinsp_logger::SEV_DEBUG); // +++ order is important - apps belong to groups and must be processed after parse_groups(std::move(*group.second), group.first); parse_apps(std::move(*app_it->second), app_it->first); m_last_marathon_refresh = now; // --- } else // framework was shut down, clear groups/apps { g_logger.log("Detected inactive Marathon framework " + group.first, sinsp_logger::SEV_DEBUG); m_state.erase_groups(group.first); // apps will go away with groups m_inactive_frameworks.insert(group.first); } group.second.reset(); app_it->second.reset(); m_json_error = false; ret = true; } else if((difftime(now, m_last_marathon_refresh) > tout_s) || m_json_error) { std::string errstr = "Detected null Marathon app (" + app_it->first + "), resetting current state."; g_logger.log(errstr, sinsp_logger::SEV_WARNING); g_json_error_log.log(app_it->first, errstr, sinsp_utils::get_current_time_ns(), "marathon-apps-state"); m_mesos_state_json.reset(); group.second.reset(); app_it->second.reset(); m_json_error = false; } } else { // must never happen throw sinsp_exception("A discrepancy found between groups and apps " "(app json for framework [" + group.first + "] not found in json map)."); } } else if((difftime(now, m_last_marathon_refresh) > tout_s) || m_json_error) { g_logger.log("Detected null Marathon group (" + group.first + "), resetting current state.", sinsp_logger::SEV_WARNING); m_mesos_state_json.reset(); json_map_type_t::iterator app_it = m_marathon_apps_json.find(group.first); if(app_it != m_marathon_apps_json.end()) { app_it->second.reset(); } m_json_error = false; } } } } else { parse_state(std::move(*m_mesos_state_json)); m_mesos_state_json.reset(); m_marathon_groups_json.clear(); m_marathon_apps_json.clear(); if(m_state_http->get_marathon_uris().size()) { rebuild_marathon_state(true); } m_json_error = false; ret = true; } } return ret; #else return true; #endif // HAS_CAPTURE } void mesos::handle_frameworks(const Json::Value& root) { const Json::Value& frameworks = root["frameworks"]; if(!frameworks.isNull()) { if(frameworks.isArray()) { if(frameworks.size()) { for(const auto& framework : frameworks) { const Json::Value& uid = framework["id"]; if(!uid.isNull() && uid.isString()) { const Json::Value& fw_name = framework["name"]; std::string name; if(!fw_name.isNull() && fw_name.isString()) { name = framework["name"].asString(); } if(!mesos_framework::is_framework_active(framework)) { framework_list_t::iterator it = m_inactive_frameworks.find(uid.asString()); if(it == m_inactive_frameworks.end()) { std::string fid = uid.asString(); m_inactive_frameworks.insert(fid); m_activated_frameworks.erase(fid); g_logger.log("Mesos framework deactivated: " + name + '[' + fid + ']', sinsp_logger::SEV_INFO); remove_framework(framework); #ifdef HAS_CAPTURE remove_framework_http(m_marathon_groups_http, fid); remove_framework_http(m_marathon_apps_http, fid); #endif // HAS_CAPTURE } } else // active framework detected { add_framework(framework); if((m_inactive_frameworks.erase(uid.asString())) || (m_activated_frameworks.find(uid.asString()) == m_activated_frameworks.end())) { g_logger.log("New or activated Mesos framework detected: " + name + " [" + uid.asString() + ']', sinsp_logger::SEV_INFO); m_activated_frameworks.insert(uid.asString()); #ifdef HAS_CAPTURE if(mesos_framework::is_root_marathon(name) && find_if(m_marathon_groups_http.begin(), m_marathon_groups_http.end(), [uid](const decltype(m_marathon_groups_http)::value_type& item) { return item.second->get_framework_id() == uid.asString(); }) == m_marathon_groups_http.end()) { init_marathon(); } #endif } } } } } else { throw sinsp_exception("No Mesos frameworks found (possibly Mesos master HA migration, will retry)."); } } else { throw sinsp_exception("Mesos frameworks entry found but not a JSON array."); } } else { throw sinsp_exception("No Mesos frameworks entry found in state."); } } void mesos::handle_slaves(const Json::Value& root) { const Json::Value& slaves = root["slaves"]; if(!slaves.isNull()) { for(const auto& slave : slaves) { add_slave(slave); } } else { g_logger.log("No frameworks found.", sinsp_logger::SEV_WARNING); } } void mesos::add_framework(const Json::Value& framework) { std::string name, uid; const Json::Value& fname = framework["name"]; const Json::Value& fid = framework["id"]; if(!fname.isNull()) { name = fname.asString(); } if(!fid.isNull()) { uid = fid.asString(); } if(!m_creation_logged) { std::ostringstream os; os << "Adding Mesos framework: [" << name << ',' << uid << ']'; g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); } m_state.emplace_framework(mesos_framework(name, uid)); add_tasks(m_state.get_frameworks().back(), framework); } void mesos::remove_framework(const Json::Value& framework) { m_state.remove_framework(framework); } #ifdef HAS_CAPTURE void mesos::remove_framework_http(marathon_http_map& http_map, const std::string& framework_id) { for(marathon_http_map::iterator it = http_map.begin(), end = http_map.end(); it != end; ++it) { if(it->second && it->second->get_framework_id() == framework_id) { http_map.erase(it); g_logger.log("Removed http for deactivated Marathon framework [" + framework_id + ']', sinsp_logger::SEV_DEBUG); return; } } } #endif // HAS_CAPTURE void mesos::add_slave(const Json::Value& slave) { std::string name, uid; const Json::Value& sname = slave["hostname"]; const Json::Value& sid = slave["id"]; if(!sname.isNull()) { name = sname.asString(); } if(!sid.isNull()) { uid = sid.asString(); } if(!m_creation_logged) { std::ostringstream os; os << "Adding Mesos slave: [" << name << ',' << uid << ']'; g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); } m_state.emplace_slave(mesos_slave(name, uid)); } void mesos::add_tasks_impl(mesos_framework& framework, const Json::Value& tasks) { if(!tasks.isNull()) { for(const auto& task : tasks) { if(mesos_task::is_task_running(task)) { mesos_task::ptr_t t = mesos_task::make_task(task); std::ostringstream os; if(t) { os << "Adding Mesos task: [" << framework.get_name() << ':' << t->get_name() << ',' << t->get_uid() << ']'; g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); m_state.add_or_replace_task(framework, t); } else { std::string name, uid, sid; Json::Value fname = task["name"]; if(!fname.isNull()) { name = fname.asString(); } Json::Value fid = task["id"]; if(!fid.isNull()) { uid = fid.asString(); } Json::Value fsid = task["slave_id"]; if(!fsid.isNull()) { sid = fsid.asString(); } os << "Failed to add Mesos task: [" << framework.get_name() << ':' << name << ',' << uid << "], running on slave " << sid; g_logger.log(os.str(), sinsp_logger::SEV_ERROR); g_json_error_log.log(framework.get_name(), os.str(), sinsp_utils::get_current_time_ns(), "add_tasks_impl"); } } } } else { std::string errstr = "Tasks is null"; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(framework.get_name(), errstr, sinsp_utils::get_current_time_ns(), "add_tasks_impl for framework"); } } void mesos::add_tasks(mesos_framework& framework, const Json::Value& f_val) { const Json::Value& tasks = f_val["tasks"]; add_tasks_impl(framework, tasks); } void mesos::check_frameworks(const json_ptr_t& json) { if(has_marathon() && json && !json->isNull()) { const Json::Value& frameworks = (*json)["frameworks"]; if(frameworks.isNull()) { throw sinsp_exception("No Mesos frameworks entry found."); } else { if(frameworks.isArray()) { if(!frameworks.size()) { throw sinsp_exception("No Mesos frameworks found (possibly Mesos master HA migration)."); } } else { throw sinsp_exception("Unexpected Mesos frameworks entry found (not an array)."); } } } } void mesos::set_state_json(json_ptr_t json, const std::string&) { bool json_error = !json || (json && json->isNull()); m_mesos_state_json = json; if(!json_error) { g_logger.log("Received state JSON", sinsp_logger::SEV_DEBUG); check_frameworks(m_mesos_state_json); } else { g_logger.log("Received invalid state JSON", sinsp_logger::SEV_WARNING); } m_json_error = m_json_error || json_error; } void mesos::parse_state(Json::Value&& root) { clear_mesos(); handle_frameworks(root); handle_slaves(root); #ifdef HAS_CAPTURE if(m_state.is_captured()) { Json::Value capt; capture_frameworks(root, capt); capture_slaves(root, capt); m_state.enqueue_capture_event(mesos_state_t::capture::MESOS_STATE, Json::FastWriter().write(capt)); } #endif // HAS_CAPTURE if(m_verbose) { std::cout << Json::FastWriter().write(root) << std::endl; } if(!m_creation_logged) { m_creation_logged = true; } } void mesos::parse_state(json_ptr_t json, const std::string&) { if(json && !json->isNull()) { parse_state(std::move(*json)); } else { throw sinsp_exception("Invalid JSON (parsing Mesos state failed)."); } } void mesos::set_marathon_groups_json(json_ptr_t json, const std::string& framework_id) { bool json_error = !json || (json && json->isNull()); m_marathon_groups_json[framework_id] = json; if(!json_error) { g_logger.log("Received groups JSON for Marathon framework [" + framework_id + ']', sinsp_logger::SEV_DEBUG); } else { g_logger.log("Received invalid Marathon groups JSON", sinsp_logger::SEV_WARNING); } m_json_error = m_json_error || json_error; } void mesos::set_marathon_apps_json(json_ptr_t json, const std::string& framework_id) { bool json_error = !json || (json && json->isNull()); m_marathon_apps_json[framework_id] = json; if(!json_error) { g_logger.log("Received apps JSON for Marathon framework [" + framework_id + ']', sinsp_logger::SEV_DEBUG); } else { std::string errstr = "Received invalid Marathon apps JSON"; g_logger.log(errstr, sinsp_logger::SEV_WARNING); g_json_error_log.log("(null)", errstr, sinsp_utils::get_current_time_ns(), "set-marathon-apps-json"); } m_json_error = m_json_error || json_error; } void mesos::simulate_event(const std::string& json) { Json::Value root; Json::Reader reader; if(reader.parse(json, root)) { Json::Value::Members members = root.getMemberNames(); for (auto& member : members) { if(member == "mesos_state") { m_discover_marathon_uris = false; parse_state(std::move(root[member])); } else if(member == "marathon_groups") { const Json::Value& frameworkId = root[member]["frameworkId"]; if(!frameworkId.isNull() && frameworkId.isString()) { m_state.parse_groups(std::move(root[member]), frameworkId.asString()); } else { throw sinsp_exception("Framework ID for Marathon groups not found during event simulation."); } } else if(member == "marathon_apps") { const Json::Value& frameworkId = root[member]["frameworkId"]; if(!frameworkId.isNull() && frameworkId.isString()) { m_state.parse_apps(std::move(root[member]), frameworkId.asString()); } else { throw sinsp_exception("Framework ID for Marathon groups not found during event simulation."); } } } } else { std::string errstr = "Could not parse json (" + reader.getFormattedErrorMessages() + ")"; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(json, errstr, sinsp_utils::get_current_time_ns(), "parse-mesos-evt"); } } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/mesos.h000066400000000000000000000165771352731327100202450ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // mesos.h // #pragma once #include "json/json.h" #include "mesos_auth.h" #include "mesos_common.h" #include "mesos_component.h" #include "mesos_http.h" #include "marathon_http.h" #include "mesos_state.h" #include "mesos_collector.h" #include "uri.h" #include #include #include class mesos : public mesos_auth { public: #ifdef HAS_CAPTURE typedef mesos_http::marathon_uri_t uri_list_t; #else typedef std::vector uri_list_t; #endif // HAS_CAPTURE typedef std::shared_ptr json_ptr_t; typedef uri::credentials_t credentials_t; typedef std::shared_ptr uri_creds_ptr_t; static const std::string default_state_uri; static const std::string default_state_api; static const std::string default_marathon_uri; static const std::string default_groups_api; static const std::string default_apps_api; static const std::string default_watch_api; static const std::string default_version_api; static const int default_timeout_ms; // constructor for testing only, not to be used in production mesos(const std::string& mesos_state_json, const std::string& marathon_groups_json, const std::string& marathon_apps_json); mesos(const std::string& state_uri, const uri_list_t& marathon_uris = uri_list_t(), bool discover_mesos_leader = false, bool discover_marathon_leader = false, const credentials_t& mesos_credentials = credentials_t(), const credentials_t& marathon_credentials = credentials_t(), int timeout_ms = default_timeout_ms, bool is_captured = false, bool verbose = false); mesos(const std::string& state_uri, const uri_list_t& marathon_uris = uri_list_t(), bool discover_mesos_leader = false, bool discover_marathon_leader = false, const credentials_t& dcos_enterprise_credentials = credentials_t(), int timeout_ms = default_timeout_ms, bool is_captured = false, bool verbose = false); virtual ~mesos(); const mesos_state_t& get_state() const; bool is_alive() const; void refresh(); void clear_mesos(); bool has_marathon() const; void clear_marathon(); void simulate_event(const std::string& json); bool collect_data(); virtual void refresh_token(); #ifdef HAS_CAPTURE const uri_list_t &marathon_uris(); void send_data_request(bool collect = true); const mesos_state_t::capture_list& get_capture_events() const; std::string dequeue_capture_event(); private: void send_mesos_data_request(); void connect_mesos(); void check_collector_status(int expected); void send_marathon_data_request(); void connect_marathon(); template bool connect(T http, typename T::element_type::callback_func_t func, int expected_connections) { if(http) { if(m_collector.has(http)) { if(!http->is_connected()) { m_collector.remove(http); } } if(!m_collector.has(http)) { http->set_parse_func(func); m_collector.add(http); } check_collector_status(expected_connections); return m_collector.has(http); } return false; } void capture_frameworks(const Json::Value& root, Json::Value& capture); void capture_slaves(const Json::Value& root, Json::Value& capture); typedef std::unordered_map marathon_http_map; void remove_framework_http(marathon_http_map& http_map, const std::string& framework_id); mesos_http::ptr_t m_state_http; marathon_http_map m_marathon_groups_http; marathon_http_map m_marathon_apps_http; mesos_collector m_collector; std::string m_mesos_uri; uri_list_t m_marathon_uris; #endif // HAS_CAPTURE private: void init(); void init_marathon(); void rebuild_mesos_state(bool full = false); void rebuild_marathon_state(bool full = false); void handle_frameworks(const Json::Value& root); void add_framework(const Json::Value& framework); void add_tasks(mesos_framework& framework, const Json::Value& f_val); void add_tasks_impl(mesos_framework& framework, const Json::Value& tasks); void handle_slaves(const Json::Value& root); void add_slave(const Json::Value& framework); void check_frameworks(const json_ptr_t& json); void set_state_json(json_ptr_t json, const std::string& dummy = ""); void parse_state(Json::Value&& root); void parse_state(json_ptr_t json, const std::string&); void set_marathon_groups_json(json_ptr_t json, const std::string& framework_id); void parse_groups(json_ptr_t json, const std::string& framework_id); void parse_groups(Json::Value&& json, const std::string& framework_id); void set_marathon_apps_json(json_ptr_t json, const std::string& framework_id); void parse_apps(json_ptr_t json, const std::string& framework_id); void parse_apps(Json::Value&& json, const std::string& framework_id); void remove_framework(const Json::Value& framework); mesos_state_t m_state; bool m_creation_logged = false; bool m_discover_mesos_leader; bool m_discover_marathon_uris; long m_timeout_ms; bool m_verbose = false; typedef std::map json_map_type_t; json_ptr_t m_mesos_state_json; json_map_type_t m_marathon_groups_json; json_map_type_t m_marathon_apps_json; time_t m_last_mesos_refresh = 0; time_t m_last_marathon_refresh = 0; bool m_json_error = false; bool m_testing = false; uri::credentials_t m_mesos_credentials; uri::credentials_t m_marathon_credentials; typedef std::unordered_set framework_list_t; framework_list_t m_inactive_frameworks; framework_list_t m_activated_frameworks; static const mesos_component::component_map m_components; friend class mesos_http; friend class marathon_http; }; inline const mesos_state_t& mesos::get_state() const { return m_state; } inline bool mesos::has_marathon() const { #ifdef HAS_CAPTURE if(m_testing) { return true; } else { return m_marathon_groups_http.size() || m_marathon_apps_http.size(); } #else return false; #endif } #ifdef HAS_CAPTURE inline const mesos_state_t::capture_list& mesos::get_capture_events() const { return m_state.get_capture_events(); } inline std::string mesos::dequeue_capture_event() { return m_state.dequeue_capture_event(); } #endif // HAS_CAPTURE inline void mesos::clear_mesos() { m_state.clear_mesos(); } inline void mesos::clear_marathon() { m_state.clear_marathon(); } inline void mesos::parse_apps(json_ptr_t json, const std::string& framework_id) { m_state.parse_apps(std::move(json), framework_id); } inline void mesos::parse_groups(json_ptr_t json, const std::string& framework_id) { m_state.parse_groups(std::move(json), framework_id); } inline void mesos::parse_apps(Json::Value&& json, const std::string& framework_id) { m_state.parse_apps(std::move(json), framework_id); } inline void mesos::parse_groups(Json::Value&& json, const std::string& framework_id) { m_state.parse_groups(std::move(json), framework_id); } sysdig-0.26.4/userspace/libsinsp/mesos_auth.cpp000066400000000000000000000066351352731327100216130ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // mesos_auth.cpp // #ifndef CYGWING_AGENT #include #include "mesos_auth.h" #include "json_error_log.h" using namespace std; mesos_auth::mesos_auth(const uri::credentials_t& dcos_enterprise_credentials, string auth_hostname, int token_refresh_interval) : m_dcos_enterprise_credentials(dcos_enterprise_credentials), m_auth_uri(string("https://") + auth_hostname + string("/acs/api/v1/auth/login")), m_token_refresh_interval(token_refresh_interval), m_last_token_refresh_s(0) { if(!m_dcos_enterprise_credentials.first.empty()) { authenticate(); } } mesos_auth::~mesos_auth() { } string mesos_auth::get_token() { refresh_token(); return m_token; } void mesos_auth::authenticate() { #ifdef HAS_CAPTURE try { sinsp_curl auth_request(m_auth_uri, "", ""); Json::FastWriter json_writer; Json::Value auth_obj; auth_obj["uid"] = m_dcos_enterprise_credentials.first; auth_obj["password"] = m_dcos_enterprise_credentials.second; auth_request.add_header("Content-Type: application/json"); auth_request.setopt(CURLOPT_POST, 1); auth_request.set_body(json_writer.write(auth_obj)); //auth_request.enable_debug(); auto response = auth_request.get_data(); if(auth_request.get_response_code() == 200) { Json::Reader json_reader; Json::Value response_obj; auto parse_ok = json_reader.parse(response, response_obj, false); if(parse_ok && response_obj.isMember("token")) { m_token = response_obj["token"].asString(); g_logger.format(sinsp_logger::SEV_DEBUG, "Mesos authenticated with token=%s", m_token.c_str()); } else if (!parse_ok) { std::string errstr; errstr = json_reader.getFormattedErrorMessages(); g_json_error_log.log(response, errstr, sinsp_utils::get_current_time_ns(), m_auth_uri.to_string()); throw sinsp_exception(string("Cannot parse json (" + errstr + ")")); } else { throw sinsp_exception(string("Cannot authenticate on Mesos master, response=") + response); } } else { throw sinsp_exception(string("Cannot authenticate on Mesos master, response_code=") + to_string(auth_request.get_response_code())); } time(&m_last_token_refresh_s); } catch(std::exception& e) { std::string errstr = "Could not fetch authentication token via " + m_auth_uri.to_string() + ": " + e.what(); g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log("", errstr, sinsp_utils::get_current_time_ns(), m_auth_uri.to_string()); } #endif // HAS_CAPTURE } void mesos_auth::refresh_token() { #ifdef HAS_CAPTURE if(!m_dcos_enterprise_credentials.first.empty()) { time_t now; time(&now); if(difftime(now, m_last_token_refresh_s) > m_token_refresh_interval) { g_logger.format(sinsp_logger::SEV_DEBUG, "Regenerating Mesos Auth token"); authenticate(); } } #endif // HAS_CAPTURE } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/mesos_auth.h000066400000000000000000000027731352731327100212570ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // mesos_auth.h // #pragma once #include "json/json.h" #include "mesos_http.h" #include "uri.h" static const uint64_t DCOS_ENTERPRISE_TOKEN_REFRESH_S = 60*60*24; // 1 day class mesos_auth { public: mesos_auth(const uri::credentials_t& dcos_enterprise_credentials = uri::credentials_t(), std::string auth_hostname = "localhost", int token_refresh_interval = DCOS_ENTERPRISE_TOKEN_REFRESH_S); virtual ~mesos_auth(); virtual void refresh_token(); // Return the current token. The token may expire at any time // after the token has been returned, so it's best to call // get_token periodically, which will internally refresh the // token if necessary. std::string get_token(); protected: std::string m_token; private: void authenticate(); const uri::credentials_t m_dcos_enterprise_credentials; uri m_auth_uri; int m_token_refresh_interval; time_t m_last_token_refresh_s; }; sysdig-0.26.4/userspace/libsinsp/mesos_collector.cpp000066400000000000000000000141621352731327100226320ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // mesos_collector.cpp // #ifndef CYGWING_AGENT #ifdef HAS_CAPTURE #include "sinsp.h" #include "sinsp_int.h" #include "mesos_collector.h" #include "mesos_http.h" #include #include #include mesos_collector::mesos_collector(bool do_loop, long timeout_ms): m_nfds(0), m_loop(do_loop), m_timeout_ms(timeout_ms), m_stopped(false) { clear(); } mesos_collector::~mesos_collector() { } void mesos_collector::clear() { FD_ZERO(&m_errfd); FD_ZERO(&m_infd); } void mesos_collector::add(std::shared_ptr handler) { int sockfd = handler->get_socket(m_timeout_ms); FD_SET(sockfd, &m_errfd); FD_SET(sockfd, &m_infd); if(sockfd > m_nfds) { m_nfds = sockfd; } m_sockets[sockfd] = handler; } bool mesos_collector::has(std::shared_ptr handler) { for(const auto& http : m_sockets) { if(http.second == handler) { return true; } } return false; } bool mesos_collector::remove(std::shared_ptr handler) { for(socket_map_t::iterator it = m_sockets.begin(); it != m_sockets.end(); ++it) { if(it->second == handler) { remove(it); return true; } } return false; } mesos_collector::socket_map_t::iterator& mesos_collector::remove(socket_map_t::iterator& it) { if(it != m_sockets.end()) { m_sockets.erase(it++); } m_nfds = 0; for(const auto& sock : m_sockets) { if(sock.first > m_nfds) { m_nfds = sock.first; } } return it; } void mesos_collector::remove_all() { clear(); m_sockets.clear(); m_nfds = 0; } bool mesos_collector::is_active() const { return subscription_count() > 0; } bool mesos_collector::is_healthy(int expected_count) const { return subscription_count() >= expected_count; } int mesos_collector::subscription_count() const { return m_sockets.size(); } void mesos_collector::get_data() { try { struct timeval tv; int res; m_stopped = false; while(!m_stopped) { tv.tv_sec = m_loop ? m_timeout_ms / 1000 : 0; tv.tv_usec = m_loop ? (m_timeout_ms % 1000) * 1000 : 0; { if(m_sockets.size()) { g_logger.log("Mesos collector number of sockets: " + std::to_string(m_sockets.size()), sinsp_logger::SEV_DEBUG); res = select(m_nfds + 1, &m_infd, NULL, &m_errfd, &tv); if(res < 0) // error { std::string err = std::string("Mesos collector select error, removing all sockets (") + strerror(errno) + ")"; g_logger.log(err, sinsp_logger::SEV_ERROR); g_json_error_log.log("", err, sinsp_utils::get_current_time_ns(), "mesos-collector-get-data"); remove_all(); } else // data or idle { for(socket_map_t::iterator it = m_sockets.begin(); it != m_sockets.end();) { if(FD_ISSET(it->first, &m_infd)) { if(it->second && !it->second->on_data()) { if(errno != EAGAIN) { std::string fid = it->second->get_framework_id(); if(!fid.empty()) { std::string errstr = "Mesos collector data handling error, removing Marathon socket for framework [" + fid + ']'; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log("", errstr, sinsp_utils::get_current_time_ns(), "mesos-collector-get-data"); } else { std::string errstr = "Mesos collector data handling error, removing Mesos state socket."; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log("", errstr, sinsp_utils::get_current_time_ns(), "mesos-collector-get-data"); } remove(it); continue; } } } else { FD_SET(it->first, &m_infd); } if(FD_ISSET(it->first, &m_errfd)) { if(errno != EAGAIN) { std::string errstr = std::string("Mesos collector select errfd: ") + strerror(errno); g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log("", errstr, sinsp_utils::get_current_time_ns(), "mesos-collector-get-data"); std::string fid; if(it->second) { it->second->on_error(errstr, true); fid = it->second->get_framework_id(); } if(!fid.empty()) { std::string errstr = "Mesos collector socket error, removing Marathon socket for framework [" + fid + ']'; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log("", errstr, sinsp_utils::get_current_time_ns(), "mesos-collector-get-data"); } else { std::string errstr = "Mesos collector socket error, removing Mesos state socket."; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log("", errstr, sinsp_utils::get_current_time_ns(), "mesos-collector-get-data"); } remove(it); continue; } } else { FD_SET(it->first, &m_errfd); } ++it; } } } else { std::string errstr = "Mesos collector is empty. Stopping."; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log("", errstr, sinsp_utils::get_current_time_ns(), "mesos-collector-get-data"); m_stopped = true; return; } } if(!m_loop) { break; } } } catch(std::exception& ex) { std::string errstr = std::string("Mesos collector error: ") + ex.what(); g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log("", errstr, sinsp_utils::get_current_time_ns(), "mesos-collector-get-data"); remove_all(); m_stopped = true; } } #endif // HAS_CAPTURE #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/mesos_collector.h000066400000000000000000000031131352731327100222710ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // mesos_collector.h // #pragma once #ifdef HAS_CAPTURE #include "mesos_common.h" #include #include class mesos_http; class mesos_collector { public: typedef std::map> socket_map_t; mesos_collector(bool do_loop = true, long timeout_ms = 1000L); ~mesos_collector(); void add(std::shared_ptr handler); void remove_all(); int subscription_count() const; void get_data(); void stop(); bool is_active() const; bool is_healthy(int expected_count) const; bool has(std::shared_ptr handler); bool remove(std::shared_ptr handler); private: void clear(); socket_map_t::iterator& remove(socket_map_t::iterator& it); socket_map_t m_sockets; fd_set m_infd; fd_set m_errfd; int m_nfds; bool m_loop; long m_timeout_ms; bool m_stopped; }; inline void mesos_collector::stop() { m_stopped = true; } #endif // HAS_CAPTURE sysdig-0.26.4/userspace/libsinsp/mesos_common.h000066400000000000000000000012111352731327100215700ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // mesos_common.h // #pragma once sysdig-0.26.4/userspace/libsinsp/mesos_component.cpp000066400000000000000000000162161352731327100226500ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // mesos_component.cpp // #ifndef CYGWING_AGENT #include "mesos_component.h" #include "marathon_component.h" #include "sinsp.h" #include "sinsp_int.h" #include "json_error_log.h" #include #include // // component // const mesos_component::component_map mesos_component::list = { { mesos_component::MESOS_FRAMEWORK, "framework" }, { mesos_component::MESOS_TASK, "task" }, { mesos_component::MESOS_SLAVE, "slave" } }; mesos_component::mesos_component(type t, const std::string& name, const std::string& uid) : m_type(t), m_name(name), m_uid(uid) { component_map::const_iterator it = list.find(t); if(it == list.end()) { throw sinsp_exception("Invalid Mesos component type: " + std::to_string(t)); } if(m_name.empty()) { throw sinsp_exception("Mesos " + it->second + " name cannot be empty"); } if(m_uid.empty()) { throw sinsp_exception("Mesos " + it->second + " uid cannot be empty"); } } mesos_component::mesos_component(const mesos_component& other): m_type(other.m_type), m_name(other.m_name), m_uid(other.m_uid), m_labels(other.m_labels) { } mesos_component::mesos_component(mesos_component&& other): m_type(other.m_type), m_name(std::move(other.m_name)), m_uid(std::move(other.m_uid)), m_labels(std::move(other.m_labels)) { } mesos_component& mesos_component::operator=(const mesos_component& other) { m_type = other.m_type; m_name = other.m_name; m_uid = other.m_uid; m_labels = other.m_labels; return *this; } mesos_component& mesos_component::operator=(const mesos_component&& other) { m_type = other.m_type; m_name = std::move(other.m_name); m_uid = std::move(other.m_uid); m_labels = other.m_labels; return *this; } std::string mesos_component::get_name(type t) { component_map::const_iterator it = list.find(t); if(it != list.end()) { return it->second; } std::ostringstream os; os << "Unknown component type " << static_cast(t); throw sinsp_exception(os.str().c_str()); } mesos_component::type mesos_component::get_type(const std::string& name) { if(name == "framework") { return MESOS_FRAMEWORK; } else if(name == "task") { return MESOS_TASK; } else if(name == "slave") { return MESOS_SLAVE; } std::ostringstream os; os << "Unknown component name " << name; throw sinsp_exception(os.str().c_str()); } mesos_pair_t* mesos_component::get_label(const mesos_pair_t& label) { for (auto& lbl : m_labels) { if((lbl.first == label.first) && (lbl.second == label.second)) { return &lbl; } } return 0; } void mesos_component::add_labels(mesos_pair_list&& labels) { for (auto& label : labels) { if(!get_label(label)) { emplace_label(std::move(label)); } } } // // framework // const std::string mesos_framework::MARATHON_ROOT_NAME = "marathon"; mesos_framework::mesos_framework(const std::string& name, const std::string& uid) : mesos_component(mesos_component::MESOS_FRAMEWORK, name, uid) { } mesos_framework::~mesos_framework() { } bool mesos_framework::is_framework_active(const Json::Value& framework) { const Json::Value& active = framework["active"]; if(!active.isNull() && active.isBool() && active.asBool()) { return true; } return false; } void mesos_framework::add_or_replace_task(std::shared_ptr task) { m_tasks.insert({task->get_uid(), task}); } void mesos_framework::remove_task(const std::string& uid) { task_map::iterator it = m_tasks.find(uid); if(it != m_tasks.end()) { m_tasks.erase(it); return; } g_logger.log("Removal of non-existing task (possible deployment failure): " + uid, sinsp_logger::SEV_WARNING); } const mesos_framework::task_map& mesos_framework::get_tasks() const { return m_tasks; } mesos_framework::task_map& mesos_framework::get_tasks() { return m_tasks; } // // task // mesos_task::mesos_task(const std::string& name, const std::string& uid) : mesos_component(mesos_component::MESOS_TASK, name, uid) { } mesos_task::~mesos_task() { } mesos_task::mesos_task(const mesos_task& other): mesos_component(other), m_marathon_app_id(other.m_marathon_app_id), m_slave_id(other.m_slave_id) { } mesos_task::mesos_task(mesos_task&& other): mesos_component(std::move(other)), m_marathon_app_id(std::move(other.m_marathon_app_id)), m_slave_id(std::move(other.m_slave_id)) { } mesos_task& mesos_task::operator=(const mesos_task& other) { mesos_component::operator =(other); return *this; } mesos_task& mesos_task::operator=(const mesos_task&& other) { mesos_component::operator =(std::move(other)); return *this; } bool mesos_task::is_task_running(const Json::Value& task) { const Json::Value& task_state = task["state"]; if(!task_state.isNull() && task_state.isString()) { return task_state.asString() == "TASK_RUNNING"; } return false; } mesos_task::ptr_t mesos_task::make_task(const Json::Value& task) { //g_logger.log(task.toStyledString(), sinsp_logger::SEV_DEBUG); std::string name, uid, sid; Json::Value fid = task["id"]; if(!fid.isNull()) { uid = fid.asString(); } else { fid = task["taskId"]; if(!fid.isNull()) { uid = fid.asString(); } } Json::Value fname = task["name"]; if(!fname.isNull()) { name = fname.asString(); } else { std::string::size_type pos = uid.rfind('.'); if(pos != std::string::npos) { name = uid.substr(0, pos); } } std::shared_ptr t(new mesos_task(name, uid)); Json::Value fsid = task["slave_id"]; if(!fsid.isNull()) { sid = fsid.asString(); } else { Json::Value fsid = task["slaveId"]; if(!fsid.isNull()) { sid = fsid.asString(); } } if(!sid.empty()) { t->set_slave_id(sid); } add_labels(t, task); return t; } void mesos_task::add_labels(mesos_task::ptr_t task, const Json::Value& t_val) { std::ostringstream os; if(task) { Json::Value labels = t_val["labels"]; if(!labels.isNull()) { for(const auto& label : labels) { std::string key, val; Json::Value lkey = label["key"]; Json::Value lval = label["value"]; if(!lkey.isNull()) { key = lkey.asString(); } if(!lval.isNull()) { val = lval.asString(); } os << "Adding Mesos task label: [" << key << ':' << val << ']'; g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); os.str(""); task->emplace_label(mesos_pair_t(key, val)); } } } else { os << "Attempt to add Mesos task labels to null task."; g_logger.log(os.str(), sinsp_logger::SEV_ERROR); g_json_error_log.log("", os.str(), sinsp_utils::get_current_time_ns(), "mesos-task-add-labels"); } } // // slave // mesos_slave::mesos_slave(const std::string& name, const std::string& uid) : mesos_component(mesos_component::MESOS_SLAVE, name, uid) { } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/mesos_component.h000066400000000000000000000133221352731327100223100ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // mesos_component.h // // mesos components (frameworks, tasks, slaves) // abstraction // #pragma once #include "json/json.h" #include "sinsp.h" #include "sinsp_int.h" #include #include #include #include typedef std::pair mesos_pair_t; typedef std::vector mesos_pair_list; // // component // class mesos_component { public: enum type { MESOS_FRAMEWORK, MESOS_TASK, MESOS_SLAVE }; typedef std::pair component_pair; typedef std::map component_map; static const component_map list; mesos_component() = delete; mesos_component(type t, const std::string& name, const std::string& uid); mesos_component(const mesos_component& other); mesos_component(mesos_component&& other); mesos_component& operator=(const mesos_component& other); mesos_component& operator=(const mesos_component&& other); const std::string& get_name() const; void set_name(const std::string& name); const std::string& get_uid() const; void set_uid(const std::string& uid); mesos_pair_t* get_label(const mesos_pair_t& label); const mesos_pair_list& get_labels() const; void set_labels(mesos_pair_list&& labels); void add_labels(mesos_pair_list&& labels); void swap_labels(mesos_pair_list& new_labels); void push_label(const mesos_pair_t& label); void emplace_label(mesos_pair_t&& label); static const std::string& get_name(const component_pair& p); static std::string get_name(type t); static type get_type(const component_pair& p); static type get_type(const std::string& name); private: type m_type; std::string m_name; std::string m_uid; mesos_pair_list m_labels; }; class mesos_framework; // // task // class mesos_task : public mesos_component { public: typedef std::shared_ptr ptr_t; mesos_task(const std::string& name, const std::string& uid); mesos_task(const mesos_task& other); ~mesos_task(); mesos_task(mesos_task&& other); mesos_task& operator=(const mesos_task& other); mesos_task& operator=(const mesos_task&& other); void set_marathon_app_id(const std::string& app_id) { m_marathon_app_id = app_id; } const std::string& get_marathon_app_id() const { return m_marathon_app_id; } void set_slave_id(const std::string& slave_id) { m_slave_id = slave_id; } const std::string& get_slave_id() const { return m_slave_id; } static bool is_task_running(const Json::Value& task); static ptr_t make_task(const Json::Value& task); static void add_labels(ptr_t task, const Json::Value& t_val); private: std::string m_marathon_app_id; std::string m_slave_id; }; // // framework // class mesos_framework : public mesos_component { public: static const std::string MARATHON_ROOT_NAME; typedef std::shared_ptr ptr_t; typedef mesos_task::ptr_t task_ptr_t; typedef std::unordered_map task_map; mesos_framework(const std::string& name, const std::string& uid); ~mesos_framework(); bool has_task(const std::string& uid) const; task_ptr_t get_task(const std::string& id); void add_or_replace_task(std::shared_ptr task); void remove_task(const std::string& uid); const task_map& get_tasks() const; task_map& get_tasks(); static bool is_framework_active(const Json::Value& framework); static bool is_root_marathon(const std::string& name); private: task_map m_tasks; }; // // slave // class mesos_slave : public mesos_component { public: mesos_slave(const std::string& name, const std::string& uid); private: }; typedef std::vector mesos_frameworks; typedef std::vector mesos_slaves; // // component // inline const std::string& mesos_component::get_name() const { return m_name; } inline void mesos_component::set_name(const std::string& name) { m_name = name; } inline const std::string& mesos_component::get_uid() const{ return m_uid; } inline void mesos_component::set_uid(const std::string& uid) { m_uid = uid; } inline const mesos_pair_list& mesos_component::get_labels() const { return m_labels; } inline void mesos_component::set_labels(mesos_pair_list&& labels) { m_labels = std::move(labels); } inline void mesos_component::swap_labels(mesos_pair_list& new_labels) { m_labels.swap(new_labels); } inline void mesos_component::push_label(const mesos_pair_t& label) { m_labels.push_back(label); } inline void mesos_component::emplace_label(mesos_pair_t&& label) { m_labels.emplace_back(label); } inline const std::string& mesos_component::get_name(const component_pair& p) { return p.second; } inline mesos_component::type mesos_component::get_type(const component_pair& p) { return p.first; } // // framework // inline bool mesos_framework::has_task(const std::string& uid) const { return m_tasks.find(uid) != m_tasks.end(); } inline mesos_framework::task_ptr_t mesos_framework::get_task(const std::string& id) { task_map::iterator it = m_tasks.find(id); if(it != m_tasks.end()) { return it->second; } return 0; } inline bool mesos_framework::is_root_marathon(const std::string& name) { return name == MARATHON_ROOT_NAME; } // // task // sysdig-0.26.4/userspace/libsinsp/mesos_http.cpp000066400000000000000000000622321352731327100216240ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // mesos_http.cpp // #ifndef CYGWING_AGENT #ifdef HAS_CAPTURE #include "mesos_http.h" #include "curl/curl.h" #include "curl/easy.h" #include "sinsp.h" #include "sinsp_int.h" #include "sinsp_curl.h" #include "json_error_log.h" #include "mesos.h" #define BUFFERSIZE 512 // b64 needs this macro #include "b64/encode.h" #include #include #include #include #include mesos_http::mesos_http(mesos& m, const uri& url, bool discover_mesos_lead_master, bool discover_marathon, int timeout_ms, const string& token): m_sync_curl(curl_easy_init()), m_select_curl(curl_easy_init()), m_mesos(m), m_url(url), m_connected(true), m_watch_socket(-1), m_timeout_ms(timeout_ms), m_callback_func(0), m_curl_version(curl_version_info(CURLVERSION_NOW)), m_is_mesos_state(url.to_string().find(mesos::default_state_api) != std::string::npos), m_discover_lead_master(discover_mesos_lead_master), m_discover_marathon(discover_marathon), m_token(token) { if(!m_sync_curl || !m_select_curl) { throw sinsp_exception("mesos_http: CURL initialization failed."); } ASSERT(m_curl_version); m_request = make_request(url, m_curl_version); if(!m_token.empty()) { m_sync_curl_headers.add(string("Authorization: token=") + m_token); check_error(curl_easy_setopt(m_sync_curl, CURLOPT_HTTPHEADER, m_sync_curl_headers.ptr())); } if(m_url.is_secure()) { check_error(curl_easy_setopt(m_sync_curl, CURLOPT_SSL_VERIFYPEER, 0)); check_error(curl_easy_setopt(m_sync_curl, CURLOPT_SSL_VERIFYHOST, 0)); check_error(curl_easy_setopt(m_select_curl, CURLOPT_SSL_VERIFYPEER, 0)); check_error(curl_easy_setopt(m_select_curl, CURLOPT_SSL_VERIFYHOST, 0)); } check_error(curl_easy_setopt(m_sync_curl, CURLOPT_FORBID_REUSE, 1L)); check_error(curl_easy_setopt(m_sync_curl, CURLOPT_CONNECTTIMEOUT_MS, m_timeout_ms)); check_error(curl_easy_setopt(m_sync_curl, CURLOPT_TIMEOUT_MS, m_timeout_ms)); check_error(curl_easy_setopt(m_select_curl, CURLOPT_CONNECTTIMEOUT_MS, m_timeout_ms)); discover_mesos_leader(); } mesos_http::~mesos_http() { cleanup(); } void mesos_http::cleanup() { cleanup(&m_sync_curl); cleanup(&m_select_curl); } void mesos_http::cleanup(CURL** curl) { if(curl && *curl) { curl_easy_cleanup(*curl); *curl = 0; } m_connected = false; } void mesos_http::set_token(const string& token) { m_token = token; m_request = make_request(m_url, m_curl_version); } Json::Value mesos_http::get_state_frameworks() { Json::Value frameworks; std::ostringstream os; CURLcode res = get_data(m_url.to_string(), os); if(res == CURLE_OK) { Json::Value root; Json::Reader reader; if(reader.parse(os.str(), root)) { frameworks = root["frameworks"]; if(frameworks.isNull() || !frameworks.isArray()) { throw sinsp_exception("mesos_http: Unexpected condition while detecting Mesos master: frameworks entry not found."); } } else { std::string errstr; errstr = reader.getFormattedErrorMessages(); g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); g_json_error_log.log(os.str(), errstr, sinsp_utils::get_current_time_ns(), m_url.to_string()); throw sinsp_exception("mesos_http: Mesos master leader detection failed in get_state_frameworks(): Invalid JSON (" + errstr + ")"); } } else { throw sinsp_exception(std::string("mesos_http: Mesos master leader [") + m_url.to_string(false) + "] detection failed: " + curl_easy_strerror(res)); } return frameworks; } void mesos_http::discover_mesos_leader() { if(m_is_mesos_state) { g_logger.log("mesos_http: Inspecting Mesos leader [" + m_url.to_string(false) + ']', sinsp_logger::SEV_DEBUG); std::ostringstream os; CURLcode res = get_data(m_url.to_string(), os); if(res == CURLE_OK) { long http_response_code = 0; check_error(curl_easy_getinfo(m_sync_curl, CURLINFO_RESPONSE_CODE, &http_response_code)); if(sinsp_curl::is_redirect(http_response_code)) { uri newurl(m_redirect); m_url.set_host(newurl.get_host()); g_logger.log("mesos_http: Detected Mesos master leader HTTP redirect: [" + m_url.to_string(false) + ']', sinsp_logger::SEV_INFO); discover_mesos_leader(); return; } Json::Value root; Json::Reader reader; if(reader.parse(os.str(), root)) { const Json::Value& frameworks = root["frameworks"]; if(frameworks.isNull() || !frameworks.isArray()) { throw sinsp_exception("mesos_http: Unexpected condition while detecting Mesos master: frameworks entry not found."); } g_logger.log("Found " + std::to_string(frameworks.size()) + " Mesos frameworks", sinsp_logger::SEV_DEBUG); if(frameworks.size()) // this is master leader { discover_framework_uris(frameworks); g_logger.log("mesos_http: Found Mesos master leader [" + m_url.to_string(false) + ']', sinsp_logger::SEV_INFO); return; } else if(!m_discover_lead_master) // this is standby server and autodiscovery is disabled { throw sinsp_exception("mesos_http: Detected standby Mesos master: autodiscovery not enabled. Giving up (will retry)."); } else // autodiscovery is enabled, find where is the master { const Json::Value& leader = root["leader"]; if(!leader.isNull() && leader.isString()) { std::string leader_address = leader.asString(); std::string::size_type pos = leader_address.find('@'); if(pos != std::string::npos && (pos + 1) < leader_address.size()) { std::string address = m_url.get_scheme() + "://"; if(!m_mesos.m_mesos_credentials.first.empty()) { address.append(m_mesos.m_mesos_credentials.first).append(1, ':').append(m_mesos.m_mesos_credentials.second).append(1, '@'); } address.append(leader_address.substr(pos + 1)).append(mesos::default_state_api); if(address != m_url.to_string(true)) { g_logger.log("mesos_http: Detected Mesos master leader redirect: [" + uri(address).to_string(false) + ']', sinsp_logger::SEV_INFO); m_url = address; discover_mesos_leader(); } else { throw sinsp_exception("mesos_http: Mesos master leader not discovered at [" + uri(address).to_string(false) + "] . " "Giving up temporarily ..."); } } else { throw sinsp_exception("mesos_http: Unexpected leader entry format while detecting Mesos master: " + leader_address); } } else { g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); throw sinsp_exception("mesos_http: Unexpected condition while detecting Mesos master leader [" + m_url.to_string(false) + "]: leader entry not found."); } } } else { std::string errstr; errstr = reader.getFormattedErrorMessages(); g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); g_json_error_log.log(os.str(), errstr, sinsp_utils::get_current_time_ns(), m_url.to_string()); throw sinsp_exception("mesos_http: Mesos master leader [" + m_url.to_string(false) + "] detection failed: Invalid JSON (" + errstr + ")"); } } else { throw sinsp_exception("mesos_http: Mesos master leader [" + m_url.to_string(false) + "] detection failed: " + curl_easy_strerror(res)); } } } std::string mesos_http::get_framework_url(const Json::Value& framework) { const Json::Value& fw_name = framework["name"]; bool is_marathon = false; if(!fw_name.isNull() && fw_name.isConvertibleTo(Json::stringValue)) { is_marathon = mesos_framework::is_root_marathon(fw_name.asString()); } bool has_creds = !m_mesos.m_marathon_credentials.first.empty(); Json::Value fw_url = framework["webui_url"]; if(!fw_url.isNull() && fw_url.isString() && !fw_url.asString().empty()) { uri url(fw_url.asString()); if(is_marathon && has_creds) { url.set_credentials(m_mesos.m_marathon_credentials); } return url.to_string(true); } else { fw_url = framework["hostname"]; if(!fw_url.isNull() && fw_url.isString() && !fw_url.asString().empty()) { uri url(std::string("http://").append(fw_url.asString()).append(":8080")); if(is_marathon && has_creds) { url.set_credentials(m_mesos.m_marathon_credentials); } return url.to_string(true); } } return ""; } bool mesos_http::is_framework_active(const Json::Value& framework) { Json::Value active = framework["active"]; if(!active.isNull() && active.isBool() && active.asBool()) { return true; } return false; } void mesos_http::discover_framework_uris(const Json::Value& frameworks) { m_marathon_uris.clear(); if(frameworks.isNull()) { throw sinsp_exception("mesos_http: Unexpected condition while inspecting Marathon framework: frameworks entry not found."); } if(frameworks.isArray()) { g_logger.log("Discovered " + std::to_string(frameworks.size()) + " frameworks.", sinsp_logger::SEV_DEBUG); for(const auto& framework : frameworks) { const Json::Value& id = framework["id"]; if(id.isNull() || !id.isString()) { throw sinsp_exception("mesos_http: Unexpected condition while detecting Marathon framework: ID entry not found."); } else { const Json::Value& active = framework["active"]; const Json::Value& fw_name = framework["name"]; std::string name; if(!fw_name.isNull() && fw_name.isString()) { name = fw_name.asString(); } g_logger.log("Examining " + name + " [" + id.asString() + "] framework.", sinsp_logger::SEV_DEBUG); if(!active.isNull() && active.isBool() && active.asBool()) { std::string framework_url = get_framework_url(framework); if(!framework_url.empty()) { if(m_discover_marathon) { if(mesos_framework::is_root_marathon(name)) { g_logger.log(std::string("mesos_http: Found Marathon framework ").append(name).append(" (").append(id.asString()).append(") at [").append(framework_url).append(1, ']'), sinsp_logger::SEV_INFO); if(!m_marathon_uris.size()) { m_marathon_uris.emplace_back(get_framework_url(framework)); } else { g_logger.log("mesos_http: Multiple marathon URIs discovered; only the first one (" + m_marathon_uris[0] + ") will have effect;" " others will be treated as generic frameworks.", sinsp_logger::SEV_WARNING); } } else { g_logger.log(std::string("mesos_http: Skipping non-Marathon framework URL detection ").append(name).append(" (").append(id.asString()).append(1, ')'), sinsp_logger::SEV_DEBUG); } } else { g_logger.log(std::string("mesos_http: Marathon detection not enabled."), sinsp_logger::SEV_DEBUG); } } else { if(m_discover_marathon && mesos_framework::is_root_marathon(name)) { std::string errstr = "mesos_http: Can not obtain URL for Marathon framework."; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log("", errstr, sinsp_utils::get_current_time_ns(), m_url.to_string()); } } } else // framework exists, but is not active - remove it if we were watching it so far { g_logger.log(std::string("mesos_http: Mesos framework ").append(name).append(" (").append(id.asString()).append(") deactivated."), sinsp_logger::SEV_INFO); std::string framework_url = get_framework_url(framework); for(marathon_uri_t::iterator it = m_marathon_uris.begin(); it != m_marathon_uris.end();) { if(framework_url == *it) { it = m_marathon_uris.erase(it); } else { ++it; } } } } } } else { throw sinsp_exception("mesos_http: Mesos master leader detection failed: " + m_url.to_string(false)); } } std::string mesos_http::make_request(uri url, curl_version_info_data* curl_version) { std::ostringstream request; std::string host_and_port = url.get_host(); int port = url.get_port(); if(port) { host_and_port.append(1, ':').append(std::to_string(port)); } request << "GET " << url.get_path(); std::string query = url.get_query(); if(!query.empty()) { request << '?' << query; } request << " HTTP/1.1\r\nConnection: Keep-Alive\r\nUser-Agent: sysdig"; if(curl_version && curl_version->version) { request << " (curl " << curl_version->version << ')'; } request << "\r\nHost: " << host_and_port << "\r\nAccept: */*\r\n"; std::string creds = url.get_credentials(); if(!creds.empty()) { std::istringstream is(creds); std::ostringstream os; base64::encoder().encode(is, os); request << "Authorization: Basic " << os.str() << "\r\n"; } if(!m_token.empty()) { request << "Authorization: token=" << m_token << "\r\n"; } request << "\r\n"; return request.str(); } CURLcode mesos_http::get_data(const std::string& url, std::ostream& os) { g_logger.log(std::string("mesos_http: Retrieving data from ") + uri(url).to_string(false), sinsp_logger::SEV_DEBUG); check_error(curl_easy_setopt(m_sync_curl, CURLOPT_URL, url.c_str())); check_error(curl_easy_setopt(m_sync_curl, CURLOPT_HEADERDATA, m_redirect)); check_error(curl_easy_setopt(m_sync_curl, CURLOPT_HEADERFUNCTION, sinsp_curl::header_callback)); check_error(curl_easy_setopt(m_sync_curl, CURLOPT_NOSIGNAL, 1)); //Prevent "longjmp causes uninitialized stack frame" bug check_error(curl_easy_setopt(m_sync_curl, CURLOPT_ACCEPT_ENCODING, "deflate")); check_error(curl_easy_setopt(m_sync_curl, CURLOPT_TIMEOUT_MS, m_timeout_ms)); check_error(curl_easy_setopt(m_sync_curl, CURLOPT_WRITEFUNCTION, sinsp_curl::write_data)); check_error(curl_easy_setopt(m_sync_curl, CURLOPT_WRITEDATA, &os)); return curl_easy_perform(m_sync_curl); } bool mesos_http::get_all_data(callback_func_t parse) { std::ostringstream os; CURLcode res = get_data(m_url.to_string(), os); if(res != CURLE_OK) { std::string errstr = std::string("Could not fetch url:") + curl_easy_strerror(res); g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log("", errstr, sinsp_utils::get_current_time_ns(), m_url.to_string()); m_connected = false; } else { // HTTP errors are not returned by curl API // error will be in the response stream long http_code = 0; curl_easy_getinfo(m_sync_curl, CURLINFO_RESPONSE_CODE, &http_code); if(http_code >= 400) { m_connected = false; return false; } else if(sinsp_curl::is_redirect(http_code)) { g_logger.log("mesos_http: HTTP redirect (" + std::to_string(http_code) + ')', sinsp_logger::SEV_DEBUG); if(sinsp_curl::handle_redirect(m_url, std::string(m_redirect), os)) { os.str(""); return get_all_data(parse); } } Json::Reader reader; json_ptr_t root(new Json::Value()); if(reader.parse(os.str(), *root)) { (m_mesos.*parse)(root, m_framework_id); } else { std::string errstr; errstr = reader.getFormattedErrorMessages(); g_logger.log("mesos_http: Mesos or Marathon Invalid JSON received from [" + m_url.to_string(false) + "]: " + errstr, sinsp_logger::SEV_WARNING); g_logger.log("JSON: <" + os.str() + '>', sinsp_logger::SEV_DEBUG); g_json_error_log.log(os.str(), errstr, sinsp_utils::get_current_time_ns(), m_url.to_string()); } m_connected = true; } return res == CURLE_OK; } int mesos_http::wait(int for_recv) { struct timeval tv; fd_set infd, outfd, errfd; int res; tv.tv_sec = m_timeout_ms / 1000; tv.tv_usec = (m_timeout_ms % 1000) * 1000; FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd); FD_SET(m_watch_socket, &errfd); if(for_recv) { FD_SET(m_watch_socket, &infd); } else { FD_SET(m_watch_socket, &outfd); } res = select(m_watch_socket + 1, &infd, &outfd, &errfd, &tv); return res; } int mesos_http::get_socket(long timeout_ms) { if(m_request.empty()) { throw sinsp_exception("mesos_http: Cannot create watch socket (request empty)."); } if(timeout_ms != -1) { m_timeout_ms = timeout_ms; } if(m_watch_socket < 0 || !m_connected) { long sockextr; std::string url = get_url().to_string(); check_error(curl_easy_setopt(m_select_curl, CURLOPT_URL, url.c_str())); check_error(curl_easy_setopt(m_select_curl, CURLOPT_CONNECT_ONLY, 1L)); #if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 25 // enable TCP keep-alive for this transfer check_error(curl_easy_setopt(m_select_curl, CURLOPT_TCP_KEEPALIVE, 1L)); // keep-alive idle time check_error(curl_easy_setopt(m_select_curl, CURLOPT_TCP_KEEPIDLE, 300L)); // interval time between keep-alive probes check_error(curl_easy_setopt(m_select_curl, CURLOPT_TCP_KEEPINTVL, 10L)); #endif // LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 25 check_error(curl_easy_perform(m_select_curl)); check_error(curl_easy_getinfo(m_select_curl, CURLINFO_LASTSOCKET, &sockextr)); m_watch_socket = sockextr; if(!wait(0)) { throw sinsp_exception("mesos_http: Error obtaining socket: timeout."); } g_logger.log(std::string("mesos_http: Connected; collecting data from ") + uri(url).to_string(false), sinsp_logger::SEV_DEBUG); } if(m_watch_socket <= 0) { throw sinsp_exception("mesos_http: Error obtaining socket: " + std::to_string(m_watch_socket)); } m_connected = true; return m_watch_socket; } void mesos_http::send_request() { if(m_request.empty()) { throw sinsp_exception("mesos_http: Mesos send request (empty)."); } if(m_watch_socket < 0) { throw sinsp_exception("mesos_http: Mesos send invalid socket."); } //size_t iolen = send(m_watch_socket, m_request.c_str(), m_request.size(), 0); size_t iolen; check_error(curl_easy_send(m_select_curl, m_request.c_str(), m_request.size(), &iolen)); if((iolen <= 0) || (m_request.size() != iolen)) { throw sinsp_exception("mesos_http: Mesos send socket connection error."); } else if(!wait(0)) { throw sinsp_exception("mesos_http: Mesos send timeout."); } g_logger.log(m_request, sinsp_logger::SEV_DEBUG); } bool purge_chunked_markers(std::string& data) { std::string::size_type pos = data.find("}\r\n0"); if(pos != std::string::npos) { data = data.substr(0, pos); } const std::string nl = "\r\n"; std::string::size_type begin, end; while((begin = data.find(nl)) != std::string::npos) { end = data.find(nl, begin + 2); if(end != std::string::npos) { data.erase(begin, end + 2 - begin); } else // newlines must come in pairs { return false; } } return true; } void mesos_http::handle_json(std::string::size_type end_pos, bool chunked) { if(end_pos != std::string::npos) { if(m_data_buf.length() >= end_pos + 1) { m_data_buf = m_data_buf.substr(0, end_pos + 1); if(chunked && !purge_chunked_markers(m_data_buf)) { std::string errstr = "mesos_http: Invalid Mesos or Marathon JSON data detected (chunked transfer)."; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(m_data_buf, errstr, sinsp_utils::get_current_time_ns(), m_url.to_string()); (m_mesos.*m_callback_func)(nullptr, m_framework_id); } else { (m_mesos.*m_callback_func)(try_parse(m_data_buf, m_url.to_string()), m_framework_id); } m_data_buf.clear(); m_content_length = std::string::npos; } } } bool mesos_http::detect_chunked_transfer(const std::string& data) { if(m_content_length == std::string::npos) { std::string::size_type cl_pos = data.find("Content-Length:"); if(cl_pos != std::string::npos) { std::string::size_type nl_pos = data.find("\r\n", cl_pos); if(nl_pos != std::string::npos) { cl_pos += std::string("Content-Length:").length(); std::string cl = data.substr(cl_pos, nl_pos - cl_pos); long len = strtol(cl.c_str(), NULL, 10); if(len == 0L || len == LONG_MAX || len == LONG_MIN || errno == ERANGE) { std::string errstr = "Invalid HTTP content length from [: " + m_url.to_string(false) + ']' + std::to_string(len); (m_mesos.*m_callback_func)(nullptr, m_framework_id); m_data_buf.clear(); g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(data, errstr, sinsp_utils::get_current_time_ns(), m_url.to_string()); return false; } else { m_content_length = static_cast(len); } } } } return true; } void mesos_http::extract_data(std::string& data) { if(!detect_chunked_transfer(data)) { string errstr = "mesos_http: An error occurred while detecting chunked transfer."; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(data, errstr, sinsp_utils::get_current_time_ns(), m_url.to_string()); return; } if(m_data_buf.empty()) { m_data_buf = data; std::string::size_type pos = m_data_buf.find("\r\n{"); if(pos != std::string::npos) // JSON begin { m_data_buf = m_data_buf.substr(pos + 2); } } else { m_data_buf.append(data); } bool chunked = (m_content_length == std::string::npos); if(chunked) { handle_json(m_data_buf.find("}\r\n0"), true); } else if (m_data_buf.length() >= m_content_length) { handle_json(m_data_buf.length() - 1, false); } return; } bool mesos_http::on_data() { if(!m_callback_func) { throw sinsp_exception("mesos_http: Cannot parse data (parse function null)."); } size_t iolen = 0; char buf[1024]; buf[0] = '\0'; std::string data; CURLcode ret; std::string errstr; try { do { check_error(ret = curl_easy_recv(m_select_curl, buf, sizeof(buf), &iolen)); if(iolen > 0) { data.append(buf, iolen); } else if(ret != CURLE_AGAIN) { goto connection_closed; } } while(iolen && ret != CURLE_AGAIN); if(data.size()) { extract_data(data); } } catch(sinsp_exception& ex) { errstr = std::string("mesos_http: Data receive error [" + m_url.to_string() + "]: ").append(ex.what()); g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(buf, errstr, sinsp_utils::get_current_time_ns(), m_url.to_string()); return false; } return true; connection_closed: errstr = "mesos_http: Mesos or Marathon API connection [" + m_url.to_string() + "] closed."; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(buf, errstr, sinsp_utils::get_current_time_ns(), m_url.to_string()); m_connected = false; return false; } void mesos_http::on_error(const std::string& /*err*/, bool /*disconnect*/) { m_connected = false; } void mesos_http::check_error(CURLcode res) { if(CURLE_OK != res && CURLE_AGAIN != res) { m_connected = false; std::ostringstream os; os << "Error: " << curl_easy_strerror(res); throw sinsp_exception(os.str()); } } std::string mesos_http::make_uri(const std::string& path) { uri url = get_url(); std::string target_uri = url.get_scheme(); target_uri.append("://"); std::string user = url.get_user(); if(!user.empty()) { target_uri.append(user).append(1, ':').append(url.get_password()).append(1, '@'); } target_uri.append(url.get_host()); int port = url.get_port(); if(port) { target_uri.append(1, ':').append(std::to_string(port)); } target_uri.append(path); return target_uri; } Json::Value mesos_http::get_task_labels(const std::string& task_id) { std::ostringstream os; std::string uri = make_uri("/master/tasks"); CURLcode res = get_data(uri, os); Json::Value labels; if(res != CURLE_OK) { std::string errstr = curl_easy_strerror(res); g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(task_id, errstr, sinsp_utils::get_current_time_ns(), uri); return labels; } try { Json::Value root; Json::Reader reader; if(reader.parse(os.str(), root, false)) { Json::Value tasks = root["tasks"]; if(!tasks.isNull()) { for(const auto& task : tasks) { Json::Value id = task["id"]; if(!id.isNull() && id.isString() && id.asString() == task_id) { Json::Value statuses = task["statuses"]; if(!statuses.isNull()) { double tstamp = 0.0; for(const auto& status : statuses) { // only task with most recent status // "TASK_RUNNING" considered Json::Value ts = status["timestamp"]; if(!ts.isNull() && ts.isNumeric() && ts.asDouble() > tstamp) { Json::Value st = status["state"]; if(!st.isNull() && st.isString()) { if(st.asString() == "TASK_RUNNING") { labels = task["labels"]; tstamp = ts.asDouble(); } else { labels.clear(); } } } } if(!labels.empty()) // currently running task found { return labels; } } } } } } else { std::string errstr; errstr = "mesos_http: Error parsing tasks (" + reader.getFormattedErrorMessages() + ")."; g_logger.log(errstr + "\nJSON:\n---\n" + os.str() + "\n---", sinsp_logger::SEV_ERROR); g_json_error_log.log(os.str(), errstr, sinsp_utils::get_current_time_ns(), uri); } } catch(std::exception& ex) { std::string errstr = std::string("mesos_http: Error parsing tasks:") + ex.what(); g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(os.str(), errstr, sinsp_utils::get_current_time_ns(), uri); } return labels; } #endif // HAS_CAPTURE #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/mesos_http.h000066400000000000000000000146401352731327100212710ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // mesos_http.h // #pragma once #ifdef HAS_CAPTURE #include "curl/curl.h" #include "uri.h" #include "json/json.h" #include #include #include #include #include "sinsp_curl.h" #include "json_error_log.h" class mesos; class mesos_http { public: typedef std::shared_ptr ptr_t; typedef std::shared_ptr json_ptr_t; typedef void (mesos::*callback_func_t)(json_ptr_t, const std::string&); typedef std::vector marathon_uri_t; mesos_http(mesos& m, const uri& url, bool discover_mesos_lead_master = false, bool discover_marathon = false, int timeout_ms = 5000L, const string& token = ""); virtual ~mesos_http(); bool get_all_data(callback_func_t); virtual int get_socket(long timeout_ms = -1); virtual bool is_connected() const; virtual bool on_data(); virtual void on_error(const std::string& err, bool disconnect); const uri& get_url() const; const std::string& get_request() const; std::string make_uri(const std::string& path); Json::Value get_task_labels(const std::string& task_id); void set_parse_func(callback_func_t parse); const std::string& get_framework_id() const; void set_framework_id(const std::string& id); const std::string& get_framework_name() const; void set_framework_name(const std::string& id); const std::string& get_framework_version() const; void set_framework_version(const std::string& id); const marathon_uri_t& get_marathon_uris() const; void set_token(const string& token); protected: CURL* get_sync_curl(); CURL* get_select_curl(); mesos& get_mesos(); CURLcode get_data(const std::string& url, std::ostream& os); void check_error(CURLcode res); void cleanup(); void cleanup(CURL**); int wait(int for_recv); callback_func_t get_parse_func(); std::string make_request(uri url, curl_version_info_data* m_curl_version = 0); static json_ptr_t try_parse(const std::string& json, const std::string &uri); static bool is_framework_active(const Json::Value& framework); std::string get_framework_url(const Json::Value& framework); private: void discover_mesos_leader(); Json::Value get_state_frameworks(); void discover_framework_uris(const Json::Value& frameworks); void send_request(); CURL* m_sync_curl; CURL* m_select_curl; mesos& m_mesos; std::string m_protocol; uri m_url; bool m_connected; curl_socket_t m_watch_socket; long m_timeout_ms; callback_func_t m_callback_func; std::string m_data_buf; std::string m_framework_id; std::string m_framework_name; std::string m_framework_version; curl_version_info_data* m_curl_version; std::string m_request; bool m_is_mesos_state; marathon_uri_t m_marathon_uris; bool m_discover_lead_master; bool m_discover_marathon; //bool m_redirect = false; std::string::size_type m_content_length = std::string::npos; char m_redirect[CURL_MAX_HTTP_HEADER] = {0}; string m_token; sinsp_curl_http_headers m_sync_curl_headers; friend class mesos; void extract_data(std::string& data); void handle_data(); bool detect_chunked_transfer(const std::string& data); void handle_json(std::string::size_type end_pos, bool chunked); }; inline bool mesos_http::is_connected() const { return m_connected; } inline const uri& mesos_http::get_url() const { return m_url; } inline CURL* mesos_http::get_sync_curl() { return m_sync_curl; } inline CURL* mesos_http::get_select_curl() { return m_select_curl; } inline mesos& mesos_http::get_mesos() { return m_mesos; } inline const std::string& mesos_http::get_request() const { return m_request; } inline void mesos_http::set_parse_func(callback_func_t parse) { m_callback_func = parse; } inline mesos_http::callback_func_t mesos_http::get_parse_func() { return m_callback_func; } inline mesos_http::json_ptr_t mesos_http::try_parse(const std::string& json, const std::string &uri) { json_ptr_t root(new Json::Value()); try { if(Json::Reader().parse(json, *root)) { return root; } else { std::string errstr; errstr = Json::Reader().getFormattedErrorMessages(); g_logger.log("mesos_http::try_parse could not parse json (" + errstr + ")", sinsp_logger::SEV_WARNING); g_json_error_log.log(json, errstr, sinsp_utils::get_current_time_ns(), uri); } } catch(const Json::Exception &e) { g_logger.log("Could not parse JSON document: " + string(e.what()), sinsp_logger::SEV_WARNING); g_json_error_log.log(json, e.what(), sinsp_utils::get_current_time_ns(), uri); } catch(...) { } return nullptr; } inline const std::string& mesos_http::get_framework_id() const { return m_framework_id; } inline void mesos_http::set_framework_id(const std::string& id) { m_framework_id = id; } inline const std::string& mesos_http::get_framework_name() const { return m_framework_name; } inline void mesos_http::set_framework_name(const std::string& name) { m_framework_name = name; } inline const std::string& mesos_http::get_framework_version() const { return m_framework_version; } inline void mesos_http::set_framework_version(const std::string& version) { m_framework_version = version; } inline const mesos_http::marathon_uri_t& mesos_http::get_marathon_uris() const { return m_marathon_uris; } #else // !HAS_CAPTURE #include "json/json.h" #include class mesos_http { public: typedef std::shared_ptr json_ptr_t; static json_ptr_t try_parse(const std::string& json, const std::string &uri) { json_ptr_t root(new Json::Value()); try { if(Json::Reader().parse(json, *root)) { return root; } } catch(...) { } return nullptr; } }; #endif // HAS_CAPTURE sysdig-0.26.4/userspace/libsinsp/mesos_state.cpp000066400000000000000000000336731352731327100217740ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // k8s_state.cpp // #ifndef CYGWING_AGENT #include "mesos_state.h" #include "sinsp.h" #include "sinsp_int.h" #include #include #include // // state // mesos_state_t::mesos_state_t(bool is_captured, bool verbose) : m_verbose(verbose) #ifdef HAS_CAPTURE , m_is_captured(is_captured) #endif // HAS_CAPTURE { } mesos_framework::task_ptr_t mesos_state_t::get_task(const std::string& uid) const { for(auto& framework : get_frameworks()) { for(auto& task : framework.get_tasks()) { if(task.first == uid) { return task.second; } } } g_logger.log("Task not found: " + uid, sinsp_logger::SEV_WARNING); return 0; } std::unordered_set mesos_state_t::get_all_task_ids() const { std::unordered_set tasks; for(const auto& framework : m_frameworks) { for(const auto& task : framework.get_tasks()) { tasks.insert(task.first); } } return tasks; } const mesos_framework::task_map& mesos_state_t::get_tasks(const std::string& framework_uid) const { for(const auto& framework : m_frameworks) { if(framework.get_uid() == framework_uid) { return framework.get_tasks(); } } throw sinsp_exception("Framework not found: " + framework_uid); } mesos_framework::task_map& mesos_state_t::get_tasks(const std::string& framework_uid) { for(auto& framework : m_frameworks) { if(framework.get_uid() == framework_uid) { return framework.get_tasks(); } } throw sinsp_exception("Framework not found: " + framework_uid); } marathon_app::ptr_t mesos_state_t::get_app(const std::string& app_id) { marathon_group::ptr_t group = get_app_group(app_id); if(group) { g_logger.log("Found group for app [" + app_id + "]: " + group->get_id(), sinsp_logger::SEV_DEBUG); return group->get_app(app_id); } return 0; } marathon_app::ptr_t mesos_state_t::get_app(mesos_task::ptr_t task) const { for(const auto& group : m_groups) { marathon_app::ptr_t app = group.second->get_app(task); if(app) { return app; } } return 0; } marathon_group::ptr_t mesos_state_t::get_group(mesos_task::ptr_t task) const { for(const auto& group : m_groups) { marathon_group::ptr_t grp = group.second->get_group(task); if(grp) { return grp; } } return 0; } marathon_group::app_ptr_t mesos_state_t::add_or_replace_app(const std::string& app_id, const std::string& group_id, const std::string& task_id) { marathon_group::app_ptr_t app = get_app(app_id); if(!app) { app = std::make_shared(app_id); g_logger.log("Created app [" + app_id + ']', sinsp_logger::SEV_DEBUG); } else { g_logger.log("Found app [" + app_id + ']', sinsp_logger::SEV_DEBUG); } if(!app) { std::string errstr = "Could not find or create app [" + app_id + ']'; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(app_id, errstr, sinsp_utils::get_current_time_ns(), "add-replace-app"); return 0; } if(!task_id.empty()) { g_logger.log("Adding task [" + task_id + "] to app [" + app_id + ']', sinsp_logger::SEV_DEBUG); add_task_to_app(app, task_id); } marathon_group::ptr_t group = get_group(group_id); if(group) { g_logger.log("Adding app [" + app_id + "] to group [" + group_id + ']', sinsp_logger::SEV_DEBUG); group->add_or_replace_app(app); } return app; } void mesos_state_t::add_task_to_app(marathon_group::app_ptr_t app, const std::string& task_id) { if(app) { mesos_framework::task_ptr_t pt = get_task(task_id); if(pt) { app->add_task(pt); } else { std::string errstr = "Task [" + task_id + "] can not be obtained (null). Task not added to app [" + app->get_id() + ']'; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(task_id, errstr, sinsp_utils::get_current_time_ns(), "add-task-to-app"); } } else { std::string errstr = "Attempt to add task [" + task_id + "] to non-existing (null) app."; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(task_id, errstr, sinsp_utils::get_current_time_ns(), "add-task-to-app"); } } marathon_group::ptr_t mesos_state_t::get_app_group(const std::string& app_id) { std::string group_id = marathon_app::get_group_id(app_id); if(!group_id.empty()) { return get_group(group_id); } return 0; } bool mesos_state_t::remove_app(const std::string& app_id) { marathon_group::ptr_t group = get_group(app_id); if(group) { return group->remove_app(app_id); } return false; } marathon_group::ptr_t mesos_state_t::get_group(const std::string& group_id) { marathon_groups::iterator it = m_groups.find(group_id); if(it != m_groups.end()) { return it->second; } else { for(auto group : m_groups) { if(marathon_group::ptr_t p_group = group.second->get_group(group_id)) { return p_group; } } } return 0; } marathon_group::ptr_t mesos_state_t::add_or_replace_group(marathon_group::ptr_t group, marathon_group::ptr_t to_group) { std::string id = group->get_id(); if(!to_group) // top level { marathon_groups::iterator it = m_groups.find(id); if(it != m_groups.end()) { m_groups.erase(it); } m_groups.insert({id, group}); } else { to_group->add_or_replace_group(group); } return group; } bool mesos_state_t::handle_groups(const Json::Value& root, marathon_group::ptr_t to_group, const std::string& framework_id) { Json::Value groups = root["groups"]; if(!groups.isNull() && groups.isArray()) { for(const auto& group : groups) { add_group(group, to_group, framework_id); } } else { g_logger.log("No groups found.", sinsp_logger::SEV_WARNING); return false; } return true; } #ifdef HAS_CAPTURE void mesos_state_t::capture_groups(const Json::Value& root, const std::string& framework_id, Json::Value& capt, bool capture_fw) { if(!m_is_captured) { return; } capt["id"] = root["id"]; const Json::Value& apps = root["apps"]; if(!apps.isNull()) { if(capture_fw) { capt["frameworkId"] = framework_id; } capt["apps"] = Json::arrayValue; for(const auto& app : apps) { Json::Value& c_app = capt["apps"].append(Json::Value()); c_app["id"] = app["id"]; // labels const Json::Value& labels = app["labels"]; if(!labels.isNull()) { c_app["labels"] = Json::objectValue; Json::Value::Members members = labels.getMemberNames(); for (auto& member : members) { c_app["labels"][member] = labels[member]; } } } } const Json::Value& groups = root["groups"]; if(!groups.isNull()) { capt["groups"] = Json::arrayValue; for(const auto& group : groups) { Json::Value& c_group = capt["groups"].append(Json::objectValue); capture_groups(group, framework_id, c_group); } } } void mesos_state_t::capture_apps(const Json::Value& root, const std::string& framework_id) { if(!m_is_captured) { return; } Json::Value capt; const Json::Value& apps = root["apps"]; if(!apps.isNull()) { capt["frameworkId"] = framework_id; capt["apps"] = Json::arrayValue; for(const auto& app : apps) { Json::Value& c_app = capt["apps"].append(Json::Value()); c_app["id"] = app["id"]; // labels const Json::Value& labels = app["labels"]; if(!labels.isNull()) { c_app["labels"] = Json::objectValue; Json::Value::Members members = labels.getMemberNames(); for (auto& member : members) { c_app["labels"][member] = labels[member]; } } // tasks const Json::Value& tasks = app["tasks"]; if(!tasks.isNull()) { c_app["tasks"] = Json::arrayValue; for(const auto& task : tasks) { Json::Value& c_task = c_app["tasks"].append(Json::objectValue); c_task["id"] = task["id"]; c_task["host"] = task["host"]; c_task["slaveId"] = task["slaveId"]; c_task["appId"] = task["appId"]; } } } } enqueue_capture_event(capture::MARATHON_APPS, Json::FastWriter().write(capt)); } #endif // HAS_CAPTURE bool mesos_state_t::parse_groups(Json::Value&& root, const std::string& framework_id) { add_group(root, 0, framework_id); #ifdef HAS_CAPTURE if(m_is_captured) { Json::Value capt; capture_groups(root, framework_id, capt, true); enqueue_capture_event(capture::MARATHON_GROUPS, Json::FastWriter().write(capt)); } #endif // HAS_CAPTURE if(m_verbose) { std::cout << Json::FastWriter().write(root) << std::endl; } return true; } bool mesos_state_t::parse_groups(json_ptr_t json, const std::string& framework_id) { if(json && !json->isNull() && !(*json)["id"].isNull()) { return parse_groups(std::move(*json), framework_id); } else { throw sinsp_exception("Marathon groups parsing failed (Invalid JSON)."); } } void mesos_state_t::erase_groups(const std::string& framework_id) { for(marathon_groups::iterator it = m_groups.begin(); it != m_groups.end();) { if(it->second->get_framework_id() == framework_id) { m_groups.erase(it++); } else { ++it; } } } void mesos_state_t::print_groups() const { for(auto& group : m_groups) { group.second->print(); } } marathon_group::ptr_t mesos_state_t::add_group(const Json::Value& group, marathon_group::ptr_t to_group, const std::string& framework_id) { const Json::Value& group_id = group["id"]; if(!group_id.isNull()) { std::string id = group_id.asString(); std::ostringstream os; os << "Adding Marathon group [" << id << ']'; if(to_group) { os << " to group [" << to_group->get_id() << ']'; } g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); marathon_group::ptr_t pg(new marathon_group(id, framework_id)); add_or_replace_group(pg, to_group); const Json::Value& apps = group["apps"]; if(!apps.isNull()) { for(const auto& app : apps) { const Json::Value& app_id = app["id"]; if(!app_id.isNull()) { const Json::Value& instances = app["instances"]; if(!instances.isNull() && instances.isInt() && instances.asInt() > 0) { marathon_app::ptr_t p_app = get_app(app_id.asString()); if(!p_app) { p_app = add_app(app, framework_id); } if(p_app) { pg->add_or_replace_app(p_app); if(!framework_id.empty()) { for(const auto& task : get_tasks(framework_id)) { if(task.second->get_marathon_app_id() == app_id.asString()) { add_task_to_app(p_app, task.first); } } } } else { std::string errstr = "An error occurred adding app [" + app_id.asString() + "] to group [" + id + ']'; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(app_id.asString(), errstr, sinsp_utils::get_current_time_ns(), "add-group"); } } } } } Json::Value groups = group["groups"]; if(!groups.isNull() && groups.isArray()) { handle_groups(group, pg, framework_id); } return pg; } return 0; } void mesos_state_t::parse_apps(Json::Value&& root, const std::string& framework_id) { const Json::Value& apps = root["apps"]; if(!apps.isNull()) { for(const auto& app : apps) { add_app(app, framework_id); } #ifdef HAS_CAPTURE if(m_is_captured) { capture_apps(root, framework_id); } #endif // HAS_CAPTURE if(m_verbose) { std::cout << Json::FastWriter().write(root) << std::endl; } } else { g_logger.log("No apps found.", sinsp_logger::SEV_WARNING); } } void mesos_state_t::parse_apps(json_ptr_t json, const std::string& framework_id) { if(json && !json->isNull()) { parse_apps(std::move(*json), framework_id); } else { throw sinsp_exception("Invalid JSON (Marathon apps parsing failed)."); } } marathon_app::ptr_t mesos_state_t::add_app(const Json::Value& app, const std::string& /*framework_id*/) { marathon_app::ptr_t p_app = 0; const Json::Value& app_id = app["id"]; if(!app_id.isNull()) { std::string id = app_id.asString(); g_logger.log("Adding Marathon app: " + id, sinsp_logger::SEV_DEBUG); std::string group_id = marathon_app::get_group_id(id); if(!group_id.empty()) { p_app = add_or_replace_app(id, group_id); if(p_app) { const Json::Value& labels = app["labels"]; if(!labels.isNull()) { p_app->set_labels(labels); } g_logger.log("Added app [" + id + "] to Marathon group: [" + group_id + ']', sinsp_logger::SEV_DEBUG); const Json::Value& tasks = app["tasks"]; if(tasks.size()) { g_logger.log("App [" + id + "] has " + std::to_string(tasks.size()) + " tasks.", sinsp_logger::SEV_DEBUG); for(const auto& task : tasks) { Json::Value task_id = task["id"]; if(!task_id.isNull()) { std::string tid = task_id.asString(); g_logger.log("Adding Mesos task ID to app [" + id + "]: " + tid, sinsp_logger::SEV_DEBUG); mesos_framework::task_ptr_t pt = get_task(task_id.asString()); if(pt) { pt->set_marathon_app_id(id); add_task_to_app(p_app, tid); } else { std::string errstr = "Marathon task not found in mesos state: " + tid; g_logger.log(errstr, sinsp_logger::SEV_WARNING); g_json_error_log.log(tid, errstr, sinsp_utils::get_current_time_ns(), "add-app"); } } } } } else { std::string errstr = "NOT added app [" + id + "] to Marathon group: [" + group_id + ']'; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(id, errstr, sinsp_utils::get_current_time_ns(), "add-app"); } } else { std::string errstr = "Could not determine group ID for app: " + id; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(id, errstr, sinsp_utils::get_current_time_ns(), "add-app"); } } return p_app; } #endif // CYGWING_AGENT sysdig-0.26.4/userspace/libsinsp/mesos_state.h000066400000000000000000000264611352731327100214360ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // mesos_state_t.h // // mesos state abstraction // #pragma once #include "mesos_component.h" #include "marathon_component.h" #include "json/json.h" #include "sinsp.h" #include "sinsp_int.h" #include "json_error_log.h" #include #include #include #include // // state // class mesos_state_t { public: typedef std::shared_ptr json_ptr_t; #ifdef HAS_CAPTURE struct capture { enum type_t { MESOS_STATE = 0, MARATHON_GROUPS = 1, MARATHON_APPS = 2 }; capture(type_t type, std::string&& data): m_type(type), m_data(std::move(data)) { } std::string to_string() { m_data.erase(std::remove_if(m_data.begin(), m_data.end(), [](char c) { return c == '\r' || c == '\n'; })); std::ostringstream os; switch(m_type) { case MESOS_STATE: os << "{\"mesos_state\":" << m_data << '}' << std::flush; break; case MARATHON_GROUPS: os << "{\"marathon_groups\":" << m_data << '}' << std::flush; break; case MARATHON_APPS: os << "{\"marathon_apps\":" << m_data << '}' << std::flush; break; } return os.str(); } type_t m_type; std::string m_data; }; typedef std::deque capture_list; const capture_list& get_capture_events() const { return m_capture; } std::string dequeue_capture_event() { std::string ret; if(m_capture.size()) { ret = m_capture.front().to_string(); m_capture.pop_front(); } return ret; } void enqueue_capture_event(capture::type_t type, std::string&& data) { if(m_is_captured) { m_capture.emplace_back(capture(type, std::move(data))); } } bool is_captured() const { return m_is_captured; } void capture_groups(const Json::Value& root, const std::string& framework_id, Json::Value& capt, bool capture_fw = false); void capture_apps(const Json::Value& root, const std::string& framework_id); #endif // HAS_CAPTURE mesos_state_t(bool is_captured = false, bool verbose = false); // // frameworks // const mesos_frameworks& get_frameworks() const; mesos_frameworks& get_frameworks(); const mesos_framework& get_framework(const std::string& framework_uid) const; mesos_framework& get_framework(const std::string& framework_uid); void push_framework(const mesos_framework& framework); void emplace_framework(mesos_framework&& framework); void remove_framework(const std::string& framework_uid); void remove_framework(const Json::Value& framework); const mesos_framework* get_framework_for_task(const std::string& task_id) const; // // tasks // std::unordered_set get_all_task_ids() const; const mesos_framework::task_map& get_tasks(const std::string& framework_uid) const; mesos_framework::task_map& get_tasks(const std::string& framework_uid); mesos_framework::task_ptr_t get_task(const std::string& uid) const; void add_or_replace_task(mesos_framework& framework, mesos_task::ptr_t task); void remove_task(mesos_framework& framework, const std::string& uid); // // slaves // const mesos_slaves& get_slaves() const; mesos_slaves& get_slaves(); const mesos_slave& get_slave(const std::string& slave_uid) const; mesos_slave& get_slave(const std::string& slave_uid); void push_slave(const mesos_slave& slave); void emplace_slave(mesos_slave&& slave); // // Marathon // void set_marathon_uri(const std::string& uri); const std::string& get_marathon_uri() const; // // Marathon apps // void parse_apps(Json::Value&& root, const std::string& framework_id); void parse_apps(json_ptr_t json, const std::string& framework_id); marathon_app::ptr_t get_app(const std::string& app_id); marathon_group::app_ptr_t add_or_replace_app(const std::string& id, const std::string& group, const std::string& task = ""); bool remove_app(const std::string& id); void add_task_to_app(marathon_group::app_ptr_t app, const std::string& task_id); marathon_app::ptr_t get_app(mesos_task::ptr_t task) const; // // Marathon groups // bool parse_groups(Json::Value&& root, const std::string& framework_id); bool parse_groups(json_ptr_t json, const std::string& framework_id); const marathon_groups& get_groups() const; marathon_groups& get_groups(); marathon_group::ptr_t get_group(const std::string& group_id); marathon_group::ptr_t get_group(mesos_task::ptr_t task) const; marathon_group::ptr_t add_or_replace_group(marathon_group::ptr_t group, marathon_group::ptr_t to_group = 0); marathon_group::ptr_t get_app_group(const std::string& app_id); void erase_groups(const std::string& framework_id); void print_groups() const; // // state // void clear_mesos(); void clear_marathon(); bool has_data() const; private: marathon_group::ptr_t add_group(const Json::Value& group, marathon_group::ptr_t to_group, const std::string& framework_id); bool handle_groups(const Json::Value& groups, marathon_group::ptr_t p_groups, const std::string& framework_id); marathon_app::ptr_t add_app(const Json::Value& app, const std::string& framework_id); mesos_frameworks m_frameworks; std::string m_marathon_uri; mesos_slaves m_slaves; marathon_groups m_groups; bool m_verbose; #ifdef HAS_CAPTURE bool m_is_captured; capture_list m_capture; #endif // HAS_CAPTURE typedef std::unordered_map task_framework_map_t; task_framework_map_t m_task_framework_cache; }; // // frameworks // inline const mesos_frameworks& mesos_state_t::get_frameworks() const { return m_frameworks; } inline mesos_frameworks& mesos_state_t::get_frameworks() { return m_frameworks; } inline const mesos_framework& mesos_state_t::get_framework(const std::string& framework_uid) const { for(const auto& framework : m_frameworks) { if(framework.get_uid() == framework_uid) { return framework; } } throw sinsp_exception("Framework not found: " + framework_uid); } inline mesos_framework& mesos_state_t::get_framework(const std::string& framework_uid) { for(auto& framework : m_frameworks) { if(framework.get_uid() == framework_uid) { return framework; } } throw sinsp_exception("Framework not found: " + framework_uid); } inline void mesos_state_t::push_framework(const mesos_framework& framework) { for(mesos_frameworks::iterator it = m_frameworks.begin(); it != m_frameworks.end();) { if(it->get_uid() == framework.get_uid()) { it = m_frameworks.erase(it); } else { ++it; } } m_frameworks.push_back(framework); } inline void mesos_state_t::emplace_framework(mesos_framework&& framework) { for(mesos_frameworks::iterator it = m_frameworks.begin(); it != m_frameworks.end();) { if(it->get_uid() == framework.get_uid()) { it = m_frameworks.erase(it); } else { ++it; } } m_frameworks.emplace_back(std::move(framework)); } inline void mesos_state_t::remove_framework(const Json::Value& framework) { const Json::Value& id = framework["id"]; if(!id.isNull() && id.isString()) { remove_framework(id.asString()); } } inline const mesos_framework* mesos_state_t::get_framework_for_task(const std::string& task_id) const { task_framework_map_t::const_iterator it = m_task_framework_cache.find(task_id); if(it != m_task_framework_cache.end()) { return it->second; } return 0; } inline void mesos_state_t::remove_framework(const std::string& framework_uid) { for(mesos_frameworks::iterator it = m_frameworks.begin(); it != m_frameworks.end(); ++it) { if(it->get_uid() == framework_uid) { for(auto& task : it->get_tasks()) { m_task_framework_cache.erase(task.first); } m_frameworks.erase(it); return; } } } // // tasks // inline void mesos_state_t::add_or_replace_task(mesos_framework& framework, mesos_task::ptr_t task) { if(task) { framework.add_or_replace_task(task); m_task_framework_cache[task->get_uid()] = &framework; } } inline void mesos_state_t::remove_task(mesos_framework& framework, const std::string& uid) { mesos_task::ptr_t task = framework.get_task(uid); if(task) { std::string app_id = task->get_marathon_app_id(); if(!app_id.empty()) { marathon_group::ptr_t group = get_app_group(app_id); if(group) { if(!group->remove_task(uid)) { std::string errstr = "Task [" + uid + "] not found in Marathon app [" + app_id + ']'; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(uid, errstr, sinsp_utils::get_current_time_ns(), "remove-task"); } } else { std::string errstr = "Group not found for Marathon app [" + app_id + "] while trying to remove task [" + uid + ']'; g_logger.log(errstr, sinsp_logger::SEV_ERROR); g_json_error_log.log(app_id, errstr, sinsp_utils::get_current_time_ns(), "remove-task"); } } else { g_logger.log("Task [" + uid + "] has no Marathon app ID.", sinsp_logger::SEV_WARNING); } } else { g_logger.log("Task [" + uid + "] not found in framework [" + framework.get_uid() + ']', sinsp_logger::SEV_WARNING); } framework.remove_task(uid); m_task_framework_cache.erase(uid); } // // slaves // inline const mesos_slaves& mesos_state_t::get_slaves() const { return m_slaves; } inline mesos_slaves& mesos_state_t::get_slaves() { return m_slaves; } inline const mesos_slave& mesos_state_t::get_slave(const std::string& slave_uid) const { for(const auto& slave : m_slaves) { if(slave.get_uid() == slave_uid) { return slave; } } throw sinsp_exception("Slave not found: " + slave_uid); } inline mesos_slave& mesos_state_t::get_slave(const std::string& slave_uid) { for(auto& slave : m_slaves) { if(slave.get_uid() == slave_uid) { return slave; } } throw sinsp_exception("Slave not found: " + slave_uid); } inline void mesos_state_t::push_slave(const mesos_slave& slave) { for(mesos_slaves::iterator it = m_slaves.begin(); it != m_slaves.end();) { if(it->get_uid() == slave.get_uid()) { it = m_slaves.erase(it); } else { ++it; } } m_slaves.push_back(slave); } inline void mesos_state_t::emplace_slave(mesos_slave&& slave) { for(mesos_slaves::iterator it = m_slaves.begin(); it != m_slaves.end();) { if(it->get_uid() == slave.get_uid()) { it = m_slaves.erase(it); } else { ++it; } } m_slaves.emplace_back(std::move(slave)); } // // Marathon // inline void mesos_state_t::set_marathon_uri(const std::string& uri) { m_marathon_uri = uri; } inline const std::string& mesos_state_t::get_marathon_uri() const { return m_marathon_uri; } // // apps // // // groups // inline const marathon_groups& mesos_state_t::get_groups() const { return m_groups; } inline marathon_groups& mesos_state_t::get_groups() { return m_groups; } // // state // inline void mesos_state_t::clear_mesos() { m_frameworks.clear(); m_slaves.clear(); } inline void mesos_state_t::clear_marathon() { m_groups.clear(); } inline bool mesos_state_t::has_data() const { return m_frameworks.size() > 0 && m_slaves.size() > 0; } sysdig-0.26.4/userspace/libsinsp/parsers.cpp000066400000000000000000003512651352731327100211250ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #ifdef _WIN32 #define NOMINMAX #include #else #include #include #include #endif // _WIN32 #include #include #include #include #include "container_engine/mesos.h" #include "sinsp.h" #include "sinsp_int.h" #include "../../driver/ppm_ringbuffer.h" #include "tracers.h" #include "parsers.h" #include "sinsp_errno.h" #include "filter.h" #include "filterchecks.h" #include "protodecoder.h" #ifdef HAS_ANALYZER #include "analyzer_int.h" #include "analyzer_thread.h" #endif #ifdef SIMULATE_DROP_MODE bool should_drop(sinsp_evt *evt); #endif #include "container_engine/docker.h" extern sinsp_protodecoder_list g_decoderlist; extern sinsp_evttables g_infotables; #if 0 sinsp_parser::sinsp_parser(sinsp *inspector) : m_inspector(inspector), m_tmp_evt(m_inspector), m_fd_listener(NULL) { m_fake_userevt = (scap_evt*)m_fake_userevt_storage; m_inspector->m_partial_tracers_pool = new simple_lifo_queue(128); sinsp_tracerparser p(inspector); p.test(); m_drop_event_flags = EF_NONE; } #else sinsp_parser::sinsp_parser(sinsp *inspector) : m_inspector(inspector), m_tmp_evt(m_inspector), m_fd_listener(NULL) { m_fake_userevt = (scap_evt*)m_fake_userevt_storage; // // Note: allocated here instead of in the sinsp constructor because sinsp_partial_tracer // is not defined in sinsp.cpp // m_inspector->m_partial_tracers_pool = new simple_lifo_queue(128); init_metaevt(m_k8s_metaevents_state, PPME_K8S_E, SP_EVT_BUF_SIZE); init_metaevt(m_mesos_metaevents_state, PPME_MESOS_E, SP_EVT_BUF_SIZE); m_drop_event_flags = EF_NONE; } #endif sinsp_parser::~sinsp_parser() { for(uint32_t j = 0; j < m_protodecoders.size(); j++) { delete m_protodecoders[j]; } while(!m_tmp_events_buffer.empty()) { auto ptr = m_tmp_events_buffer.top(); free(ptr); m_tmp_events_buffer.pop(); } m_protodecoders.clear(); free(m_k8s_metaevents_state.m_piscapevt); free(m_mesos_metaevents_state.m_piscapevt); if(m_inspector->m_partial_tracers_pool != NULL) { delete m_inspector->m_partial_tracers_pool; } } void sinsp_parser::init_scapevt(metaevents_state& evt_state, uint16_t evt_type, uint16_t buf_size) { evt_state.m_piscapevt = (scap_evt*) realloc(evt_state.m_piscapevt, buf_size); evt_state.m_scap_buf_size = buf_size; evt_state.m_piscapevt->type = evt_type; evt_state.m_metaevt.m_pevt = evt_state.m_piscapevt; } void sinsp_parser::init_metaevt(metaevents_state& evt_state, uint16_t evt_type, uint16_t buf_size) { evt_state.m_piscapevt = 0; init_scapevt(evt_state, evt_type, buf_size); evt_state.m_metaevt.m_inspector = m_inspector; evt_state.m_metaevt.m_info = &(g_infotables.m_event_info[PPME_SYSDIGEVENT_X]); evt_state.m_metaevt.m_cpuid = 0; evt_state.m_metaevt.m_evtnum = 0; evt_state.m_metaevt.m_fdinfo = NULL; } /////////////////////////////////////////////////////////////////////////////// // PROCESSING ENTRY POINT /////////////////////////////////////////////////////////////////////////////// void sinsp_parser::process_event(sinsp_evt *evt) { uint16_t etype = evt->m_pevt->type; bool is_live = m_inspector->is_live(); // // Cleanup the event-related state // reset(evt); // // When debug mode is not enabled, filter out events about sysdig itself // #if defined(HAS_CAPTURE) if(is_live && !m_inspector->is_debug_enabled()) { if(evt->get_tid() == m_inspector->m_sysdig_pid && etype != PPME_SCHEDSWITCH_1_E && etype != PPME_SCHEDSWITCH_6_E && etype != PPME_DROP_E && etype != PPME_DROP_X && etype != PPME_SYSDIGEVENT_E && etype != PPME_PROCINFO_E && etype != PPME_CPU_HOTPLUG_E && m_inspector->m_sysdig_pid) { evt->m_filtered_out = true; return; } } #endif if(m_drop_event_flags) { enum ppm_event_flags flags; uint16_t etype = evt->m_pevt->type; if(etype == PPME_GENERIC_E || etype == PPME_GENERIC_X) { sinsp_evt_param *parinfo = evt->get_param(0); uint16_t evid = *(uint16_t *)parinfo->m_val; flags = g_infotables.m_syscall_info_table[evid].flags; } else { flags = evt->get_info_flags(); } if (flags & m_drop_event_flags) { evt->m_filtered_out = true; return; } } // // Filtering // #if defined(HAS_FILTERING) && defined(HAS_CAPTURE_FILTERING) bool do_filter_later = false; if(m_inspector->m_filter || m_inspector->m_evttype_filter) { ppm_event_flags eflags = evt->get_info_flags(); if(etype == PPME_SYSCALL_WRITE_X) { // // Check if this is a tracer // sinsp_fdinfo_t* fdinfo = evt->m_fdinfo; if(fdinfo == NULL && evt->m_tinfo != nullptr) { fdinfo = evt->m_tinfo->get_fd(evt->m_tinfo->m_lastevent_fd); evt->m_fdinfo = fdinfo; } if(fdinfo && (fdinfo->m_flags & (sinsp_fdinfo_t::FLAGS_IS_TRACER_FD | sinsp_fdinfo_t::FLAGS_IS_TRACER_FILE))) { eflags = (ppm_event_flags)(((uint64_t)eflags) | EF_MODIFIES_STATE); } else { if(!m_inspector->is_live()) { if((evt->get_dump_flags() & SCAP_DF_TRACER) != 0) { evt->m_fdinfo = NULL; eflags = (ppm_event_flags)(((uint64_t)eflags) | EF_MODIFIES_STATE); } } } } if(eflags & EF_MODIFIES_STATE) { do_filter_later = true; } else { if(m_inspector->run_filters_on_evt(evt) == false) { if(evt->m_tinfo != NULL) { if(!(eflags & EF_SKIPPARSERESET || etype == PPME_SCHEDSWITCH_6_E)) { evt->m_tinfo->m_lastevent_type = PPM_EVENT_MAX; } } evt->m_filtered_out = true; return; } } } evt->m_filtered_out = false; #endif // // Route the event to the proper function // switch(etype) { case PPME_SOCKET_SENDTO_E: if((evt->m_fdinfo == nullptr) && (evt->m_tinfo != nullptr)) { infer_sendto_fdinfo(evt); } // FALLTHRU case PPME_SYSCALL_OPEN_E: case PPME_SOCKET_SOCKET_E: case PPME_SYSCALL_EVENTFD_E: case PPME_SYSCALL_CHDIR_E: case PPME_SYSCALL_FCHDIR_E: case PPME_SYSCALL_CREAT_E: case PPME_SYSCALL_OPENAT_E: case PPME_SOCKET_SHUTDOWN_E: case PPME_SYSCALL_GETRLIMIT_E: case PPME_SYSCALL_SETRLIMIT_E: case PPME_SYSCALL_PRLIMIT_E: case PPME_SOCKET_SENDMSG_E: case PPME_SYSCALL_SENDFILE_E: case PPME_SYSCALL_SETRESUID_E: case PPME_SYSCALL_SETRESGID_E: case PPME_SYSCALL_SETUID_E: case PPME_SYSCALL_SETGID_E: case PPME_SYSCALL_EXECVE_18_E: case PPME_SYSCALL_EXECVE_19_E: case PPME_SYSCALL_SETPGID_E: store_event(evt); break; case PPME_SYSCALL_WRITE_E: if(!m_inspector->m_is_dumping && evt->m_tinfo != nullptr) { evt->m_fdinfo = evt->m_tinfo->get_fd(evt->m_tinfo->m_lastevent_fd); if(evt->m_fdinfo) { if(evt->m_fdinfo->m_flags & sinsp_fdinfo_t::FLAGS_IS_TRACER_FD) { evt->m_filtered_out = true; return; } } } break; case PPME_SYSCALL_READ_X: case PPME_SYSCALL_WRITE_X: case PPME_SOCKET_RECV_X: case PPME_SOCKET_SEND_X: case PPME_SOCKET_RECVFROM_X: case PPME_SOCKET_RECVMSG_X: case PPME_SOCKET_SENDTO_X: case PPME_SOCKET_SENDMSG_X: case PPME_SYSCALL_READV_X: case PPME_SYSCALL_WRITEV_X: case PPME_SYSCALL_PREAD_X: case PPME_SYSCALL_PWRITE_X: case PPME_SYSCALL_PREADV_X: case PPME_SYSCALL_PWRITEV_X: parse_rw_exit(evt); break; case PPME_SYSCALL_SENDFILE_X: parse_sendfile_exit(evt); break; case PPME_SYSCALL_OPEN_X: case PPME_SYSCALL_CREAT_X: case PPME_SYSCALL_OPENAT_X: case PPME_SYSCALL_OPENAT_2_X: parse_open_openat_creat_exit(evt); break; case PPME_SYSCALL_SELECT_E: case PPME_SYSCALL_POLL_E: case PPME_SYSCALL_PPOLL_E: case PPME_SYSCALL_EPOLLWAIT_E: parse_select_poll_epollwait_enter(evt); break; case PPME_SYSCALL_CLONE_11_X: case PPME_SYSCALL_CLONE_16_X: case PPME_SYSCALL_CLONE_17_X: case PPME_SYSCALL_CLONE_20_X: case PPME_SYSCALL_FORK_X: case PPME_SYSCALL_FORK_17_X: case PPME_SYSCALL_FORK_20_X: case PPME_SYSCALL_VFORK_X: case PPME_SYSCALL_VFORK_17_X: case PPME_SYSCALL_VFORK_20_X: parse_clone_exit(evt); break; case PPME_SYSCALL_EXECVE_8_X: case PPME_SYSCALL_EXECVE_13_X: case PPME_SYSCALL_EXECVE_14_X: case PPME_SYSCALL_EXECVE_15_X: case PPME_SYSCALL_EXECVE_16_X: case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_19_X: parse_execve_exit(evt); break; case PPME_PROCEXIT_E: case PPME_PROCEXIT_1_E: parse_thread_exit(evt); break; case PPME_SYSCALL_PIPE_X: parse_pipe_exit(evt); break; case PPME_SOCKET_SOCKET_X: parse_socket_exit(evt); break; case PPME_SOCKET_BIND_X: parse_bind_exit(evt); break; case PPME_SOCKET_CONNECT_X: parse_connect_exit(evt); break; case PPME_SOCKET_ACCEPT_X: case PPME_SOCKET_ACCEPT_5_X: case PPME_SOCKET_ACCEPT4_X: case PPME_SOCKET_ACCEPT4_5_X: parse_accept_exit(evt); break; case PPME_SYSCALL_CLOSE_E: parse_close_enter(evt); break; case PPME_SYSCALL_CLOSE_X: parse_close_exit(evt); break; case PPME_SYSCALL_FCNTL_E: parse_fcntl_enter(evt); break; case PPME_SYSCALL_FCNTL_X: parse_fcntl_exit(evt); break; case PPME_SYSCALL_EVENTFD_X : parse_eventfd_exit(evt); break; case PPME_SYSCALL_CHDIR_X: parse_chdir_exit(evt); break; case PPME_SYSCALL_FCHDIR_X: parse_fchdir_exit(evt); break; case PPME_SYSCALL_GETCWD_X: parse_getcwd_exit(evt); break; case PPME_SOCKET_SHUTDOWN_X: parse_shutdown_exit(evt); break; case PPME_SYSCALL_DUP_X: parse_dup_exit(evt); break; case PPME_SYSCALL_SIGNALFD_X: parse_signalfd_exit(evt); break; case PPME_SYSCALL_TIMERFD_CREATE_X: parse_timerfd_create_exit(evt); break; case PPME_SYSCALL_INOTIFY_INIT_X: parse_inotify_init_exit(evt); break; case PPME_SYSCALL_GETRLIMIT_X: case PPME_SYSCALL_SETRLIMIT_X: parse_getrlimit_setrlimit_exit(evt); break; case PPME_SYSCALL_PRLIMIT_X: parse_prlimit_exit(evt); break; case PPME_SOCKET_SOCKETPAIR_X: parse_socketpair_exit(evt); break; case PPME_SCHEDSWITCH_1_E: case PPME_SCHEDSWITCH_6_E: parse_context_switch(evt); break; case PPME_SYSCALL_BRK_4_X: case PPME_SYSCALL_MMAP_X: case PPME_SYSCALL_MMAP2_X: case PPME_SYSCALL_MUNMAP_X: parse_brk_munmap_mmap_exit(evt); break; case PPME_SYSCALL_SETRESUID_X: parse_setresuid_exit(evt); break; case PPME_SYSCALL_SETRESGID_X: parse_setresgid_exit(evt); break; case PPME_SYSCALL_SETUID_X: parse_setuid_exit(evt); break; case PPME_SYSCALL_SETGID_X: parse_setgid_exit(evt); break; case PPME_CONTAINER_E: parse_container_evt(evt); // deprecated, only here for backwards compatibility break; case PPME_CONTAINER_JSON_E: parse_container_json_evt(evt); break; case PPME_CPU_HOTPLUG_E: parse_cpu_hotplug_enter(evt); break; #ifndef CYGWING_AGENT case PPME_K8S_E: if(!m_inspector->is_live()) { parse_k8s_evt(evt); } break; case PPME_MESOS_E: if(!m_inspector->is_live()) { parse_mesos_evt(evt); } break; #endif case PPME_SYSCALL_CHROOT_X: parse_chroot_exit(evt); break; case PPME_SYSCALL_SETSID_X: parse_setsid_exit(evt); break; case PPME_SOCKET_GETSOCKOPT_X: if(evt->get_num_params() > 0) { parse_getsockopt_exit(evt); } break; default: break; } // // With some state-changing events like clone, execve and open, we do the // filtering after having updated the state // #if defined(HAS_FILTERING) && defined(HAS_CAPTURE_FILTERING) if(do_filter_later) { if(m_inspector->run_filters_on_evt(evt) == false) { evt->m_filtered_out = true; return; } evt->m_filtered_out = false; } #endif // // Offline captures can produce events with the SCAP_DF_STATE_ONLY. They are // supposed to go through the engine, but they must be filtered out before // reaching the user. // if(!is_live) { if(evt->get_dump_flags() & SCAP_DF_STATE_ONLY) { evt->m_filtered_out = true; } } // Check to see if the name changed as a side-effect of // parsing this event. Try to avoid the overhead of a string // compare for every event. if(evt->m_fdinfo) { evt->set_fdinfo_name_changed(evt->m_fdinfo->m_name != evt->m_fdinfo->m_oldname); } } void sinsp_parser::event_cleanup(sinsp_evt *evt) { if(evt->get_direction() == SCAP_ED_OUT && evt->m_tinfo && evt->m_tinfo->m_lastevent_data) { free_event_buffer(evt->m_tinfo->m_lastevent_data); evt->m_tinfo->m_lastevent_data = NULL; evt->m_tinfo->set_lastevent_data_validity(false); } } /////////////////////////////////////////////////////////////////////////////// // HELPERS /////////////////////////////////////////////////////////////////////////////// // // Called before starting the parsing. // Returns false in case of issues resetting the state. // bool sinsp_parser::reset(sinsp_evt *evt) { // // Before anything can happen, the event needs to be initialized // evt->init(); ppm_event_flags eflags = evt->get_info_flags(); uint16_t etype = evt->get_type(); evt->m_fdinfo = NULL; evt->m_errorcode = 0; // // Ignore scheduler events // if(eflags & EF_SKIPPARSERESET) { if(etype == PPME_PROCINFO_E) { evt->m_tinfo = m_inspector->get_thread(evt->m_pevt->tid, false, false); } else { evt->m_tinfo = NULL; } return false; } // // Find the thread info // // // If we're exiting a clone or if we have a scheduler event // (many kernel thread), we don't look for /proc // bool query_os; if(etype == PPME_SYSCALL_CLONE_11_X || etype == PPME_SYSCALL_CLONE_16_X || etype == PPME_SYSCALL_CLONE_17_X || etype == PPME_SYSCALL_CLONE_20_X || etype == PPME_SYSCALL_FORK_X || etype == PPME_SYSCALL_FORK_17_X || etype == PPME_SYSCALL_FORK_20_X || etype == PPME_SYSCALL_VFORK_X || etype == PPME_SYSCALL_VFORK_17_X || etype == PPME_SYSCALL_VFORK_20_X || etype == PPME_SCHEDSWITCH_6_E) { query_os = false; } else { query_os = true; } if(etype == PPME_CONTAINER_JSON_E) { evt->m_tinfo = nullptr; return true; } else { evt->m_tinfo = m_inspector->get_thread(evt->m_pevt->tid, query_os, false); } if(etype == PPME_SCHEDSWITCH_6_E) { return false; } if(!evt->m_tinfo) { if(etype == PPME_SYSCALL_CLONE_11_X || etype == PPME_SYSCALL_CLONE_16_X || etype == PPME_SYSCALL_CLONE_17_X || etype == PPME_SYSCALL_CLONE_20_X || etype == PPME_SYSCALL_FORK_X || etype == PPME_SYSCALL_FORK_17_X || etype == PPME_SYSCALL_FORK_20_X || etype == PPME_SYSCALL_VFORK_X || etype == PPME_SYSCALL_VFORK_17_X || etype == PPME_SYSCALL_VFORK_20_X) { #ifdef GATHER_INTERNAL_STATS m_inspector->m_thread_manager->m_failed_lookups->decrement(); #endif } else { ASSERT(false); } return false; } if(query_os) { evt->m_tinfo->m_flags |= PPM_CL_ACTIVE; } if(PPME_IS_ENTER(etype)) { evt->m_tinfo->m_lastevent_fd = -1; evt->m_tinfo->m_lastevent_type = etype; if(eflags & EF_USES_FD) { sinsp_evt_param *parinfo; // // Get the fd. // The fd is always the first parameter of the enter event. // parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); ASSERT(evt->get_param_info(0)->type == PT_FD); evt->m_tinfo->m_lastevent_fd = *(int64_t *)parinfo->m_val; evt->m_fdinfo = evt->m_tinfo->get_fd(evt->m_tinfo->m_lastevent_fd); } evt->m_tinfo->m_latency = 0; evt->m_tinfo->m_last_latency_entertime = evt->get_ts(); } else { sinsp_threadinfo* tinfo = evt->m_tinfo; // // event latency // if(tinfo->m_last_latency_entertime != 0) { tinfo->m_latency = evt->get_ts() - tinfo->m_last_latency_entertime; ASSERT((int64_t)tinfo->m_latency >= 0); } if(etype == tinfo->m_lastevent_type + 1) { tinfo->set_lastevent_data_validity(true); } else { tinfo->set_lastevent_data_validity(false); if(tinfo->m_lastevent_type != PPME_TRACER_E) { return false; } } // // Error detection logic // if(evt->get_num_params() != 0 && ((evt->m_info->params[0].name[0] == 'r' && evt->m_info->params[0].name[1] == 'e' && evt->m_info->params[0].name[2] == 's' && evt->m_info->params[0].name[3] == '\0') || (evt->m_info->params[0].name[0] == 'f' && evt->m_info->params[0].name[1] == 'd' && evt->m_info->params[0].name[2] == '\0'))) { sinsp_evt_param *parinfo; parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); int64_t res = *(int64_t *)parinfo->m_val; if(res < 0) { evt->m_errorcode = -(int32_t)res; } } // // Retrieve the fd // if(eflags & EF_USES_FD) { evt->m_fdinfo = tinfo->get_fd(tinfo->m_lastevent_fd); if(evt->m_fdinfo == NULL) { return false; } if(evt->m_errorcode != 0 && m_fd_listener) { m_fd_listener->on_error(evt); } if(evt->m_fdinfo->m_flags & sinsp_fdinfo_t::FLAGS_CLOSE_CANCELED) { // // A close gets canceled when the same fd is created successfully between // close enter and close exit. // If that happens // erase_fd_params eparams; evt->m_fdinfo->m_flags &= ~sinsp_fdinfo_t::FLAGS_CLOSE_CANCELED; eparams.m_fd = CANCELED_FD_NUMBER; eparams.m_fdinfo = tinfo->get_fd(CANCELED_FD_NUMBER); // // Remove the fd from the different tables // eparams.m_remove_from_table = true; eparams.m_inspector = m_inspector; eparams.m_tinfo = tinfo; eparams.m_ts = evt->get_ts(); erase_fd(&eparams); } } } return true; } void sinsp_parser::store_event(sinsp_evt *evt) { if(!evt->m_tinfo) { // // No thread in the table. We won't store this event, which mean that // we won't be able to parse the corresponding exit event and we'll have // to drop the information it carries. // #ifdef GATHER_INTERNAL_STATS m_inspector->m_stats.m_n_store_drops++; #endif return; } uint32_t elen; // // Make sure the event data is going to fit // elen = scap_event_getlen(evt->m_pevt); if(elen > SP_EVT_BUF_SIZE) { ASSERT(false); return; } // // Copy the data // auto tinfo = evt->m_tinfo; if(tinfo->m_lastevent_data == NULL) { tinfo->m_lastevent_data = reserve_event_buffer(); } memcpy(tinfo->m_lastevent_data, evt->m_pevt, elen); tinfo->m_lastevent_cpuid = evt->get_cpuid(); #ifdef GATHER_INTERNAL_STATS m_inspector->m_stats.m_n_stored_evts++; #endif } bool sinsp_parser::retrieve_enter_event(sinsp_evt *enter_evt, sinsp_evt *exit_evt) { // // Make sure there's a valid thread info // if(!exit_evt->m_tinfo) { return false; } // // Retrieve the copy of the enter event and initialize it // if(!(exit_evt->m_tinfo->is_lastevent_data_valid() && exit_evt->m_tinfo->m_lastevent_data)) { // // This happen especially at the beginning of trace files, where events // can be truncated // #ifdef GATHER_INTERNAL_STATS m_inspector->m_stats.m_n_retrieve_drops++; #endif return false; } enter_evt->init(exit_evt->m_tinfo->m_lastevent_data, exit_evt->m_tinfo->m_lastevent_cpuid); // // Make sure that we're using the right enter event, to prevent inconsistencies when events // are dropped // if(enter_evt->get_type() != (exit_evt->get_type() - 1)) { //ASSERT(false); exit_evt->m_tinfo->set_lastevent_data_validity(false); #ifdef GATHER_INTERNAL_STATS m_inspector->m_stats.m_n_retrieve_drops++; #endif return false; } #ifdef GATHER_INTERNAL_STATS m_inspector->m_stats.m_n_retrieved_evts++; #endif return true; } sinsp_protodecoder* sinsp_parser::add_protodecoder(string decoder_name) { // // Make sure this decoder is not present yet // vector::iterator it; for(it = m_protodecoders.begin(); it != m_protodecoders.end(); ++it) { if((*it)->get_name() == decoder_name) { return (*it); } } sinsp_protodecoder* nd = g_decoderlist.new_protodecoder_from_name(decoder_name, m_inspector); nd->init(); m_protodecoders.push_back(nd); return nd; } void sinsp_parser::register_event_callback(sinsp_pd_callback_type etype, sinsp_protodecoder* dec) { switch(etype) { case CT_OPEN: m_open_callbacks.push_back(dec); break; case CT_CONNECT: m_connect_callbacks.push_back(dec); break; default: ASSERT(false); break; } return; } /////////////////////////////////////////////////////////////////////////////// // PARSERS /////////////////////////////////////////////////////////////////////////////// void sinsp_parser::parse_clone_exit(sinsp_evt *evt) { sinsp_evt_param* parinfo; int64_t tid = evt->get_tid(); int64_t childtid; bool is_inverted_clone = false; // true if clone() in the child returns before the one in the parent bool tid_collision = false; bool valid_parent = true; bool in_container = false; int64_t vtid = tid; int64_t vpid = -1; uint16_t etype = evt->get_type(); // // Validate the return value and get the child tid // parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); childtid = *(int64_t *)parinfo->m_val; switch(evt->get_type()) { case PPME_SYSCALL_CLONE_11_X: parinfo = evt->get_param(8); break; case PPME_SYSCALL_CLONE_16_X: case PPME_SYSCALL_FORK_X: case PPME_SYSCALL_VFORK_X: parinfo = evt->get_param(13); break; case PPME_SYSCALL_CLONE_17_X: case PPME_SYSCALL_FORK_17_X: case PPME_SYSCALL_VFORK_17_X: parinfo = evt->get_param(14); break; case PPME_SYSCALL_CLONE_20_X: case PPME_SYSCALL_FORK_20_X: case PPME_SYSCALL_VFORK_20_X: parinfo = evt->get_param(15); break; default: ASSERT(false); } ASSERT(parinfo->m_len == sizeof(int32_t)); uint32_t flags = *(int32_t *)parinfo->m_val; if(childtid < 0) { // // clone() failed. Do nothing and keep going. // return; } // // Get the vtid to check if the clone is within a container // switch(etype) { case PPME_SYSCALL_CLONE_11_X: case PPME_SYSCALL_CLONE_16_X: case PPME_SYSCALL_CLONE_17_X: case PPME_SYSCALL_FORK_X: case PPME_SYSCALL_FORK_17_X: case PPME_SYSCALL_VFORK_X: case PPME_SYSCALL_VFORK_17_X: break; case PPME_SYSCALL_CLONE_20_X: case PPME_SYSCALL_FORK_20_X: case PPME_SYSCALL_VFORK_20_X: parinfo = evt->get_param(18); ASSERT(parinfo->m_len == sizeof(int64_t)); vtid = *(int64_t *)parinfo->m_val; parinfo = evt->get_param(19); ASSERT(parinfo->m_len == sizeof(int64_t)); vpid = *(int64_t *)parinfo->m_val; break; default: ASSERT(false); } // the flag check should always suffice but leave the tid/vtid check for older driver versions if(flags & PPM_CL_CHILD_IN_PIDNS || tid != vtid) { in_container = true; } if(childtid == 0) { // // clone() returns 0 in the child. // int64_t parenttid; // // Before embarking in parsing the event, check if there's already // an entry in the thread table for this process. If there is one, make sure // it was created recently. Otherwise, assume it's an old thread for which // we lost the exit event and remove it from the table. // if(evt->m_tinfo && evt->m_tinfo->m_clone_ts != 0) { if(evt->get_ts() - evt->m_tinfo->m_clone_ts > CLONE_STALE_TIME_NS) { m_inspector->remove_thread(tid, true); evt->m_tinfo = NULL; } } // // Check if this is a process or a new thread // if(flags & PPM_CL_CLONE_THREAD) { // // This is a thread, the parent tid is the pid // parinfo = evt->get_param(4); ASSERT(parinfo->m_len == sizeof(int64_t)); parenttid = *(int64_t *)parinfo->m_val; } else { // // This is not a thread, the parent tid is ptid // parinfo = evt->get_param(5); ASSERT(parinfo->m_len == sizeof(int64_t)); parenttid = *(int64_t *)parinfo->m_val; } // Validate that the child thread info has actually been created. // if(!evt->m_tinfo) { // // No thread yet. // This happens if // - clone() returns in the child before than in the parent. // - we dropped the clone exit event in the parent. // - clone was executed in a container // In both cases, we create the thread entry here // // XXX: inverted_clone flag should be useless for containers // since just the child's clone is allowed to create a thread // is_inverted_clone = true; // // The tid to add is the one that generated this event // childtid = tid; tid = parenttid; // // Keep going and add the event with the standard code below // } else { // // We are in the child's clone. If we are in a container, make // sure the vtid/vpid are reflected because the father was maybe // running outside the container so created the child thread without // knowing the internal vtid/vpid // if(in_container) { evt->m_tinfo->m_vtid = vtid; evt->m_tinfo->m_vpid = vpid; } return; } } else { // // We are in the father. If the father is running in a container, // don't create the child process but wait until we see child, because // the father just sees the internal tid of the child // if(in_container) { return; } } // // Lookup the thread that called clone() so we can copy its information // sinsp_threadinfo* ptinfo = m_inspector->get_thread(tid, true, true); if(NULL == ptinfo) { // // No clone() caller, we probably missed earlier events. // We simply return and ignore the event, which means this thread won't be added to the table. // ASSERT(false); return; } if(ptinfo->m_comm == "" && ptinfo->m_uid == 0xffffffff) { valid_parent = false; } // // See if the child is already there // sinsp_threadinfo* child = m_inspector->get_thread(childtid, false, true); if(NULL != child) { // // If this was an inverted clone, all is fine, we've already taken care // of adding the thread table entry in the child. // Otherwise, we assume that the entry is there because we missed the exit event // for a previous thread and we replace the info structure. // if(child->m_flags & PPM_CL_CLONE_INVERTED) { return; } else { m_inspector->remove_thread(childtid, true); tid_collision = true; } } // // Allocate the new thread info and initialize it // XXX this should absolutely not do a malloc, but get the item from a // preallocated list // sinsp_threadinfo* tinfo = new sinsp_threadinfo(m_inspector); // // Set the tid and parent tid // tinfo->m_tid = childtid; tinfo->m_ptid = tid; if(valid_parent) { // Copy the command name from the parent tinfo->m_comm = ptinfo->m_comm; // Copy the full executable name from the parent tinfo->m_exe = ptinfo->m_exe; // Copy the full executable path from the parent tinfo->m_exepath = ptinfo->m_exepath; // Copy the command arguments from the parent tinfo->m_args = ptinfo->m_args; // Copy the root from the parent tinfo->m_root = ptinfo->m_root; // Copy the session id from the parent tinfo->m_sid = ptinfo->m_sid; // Copy the process group id from the parent tinfo->m_vpgid = ptinfo->m_vpgid; tinfo->m_tty = ptinfo->m_tty; tinfo->m_loginuid = ptinfo->m_loginuid; if(!(flags & PPM_CL_CLONE_THREAD)) { tinfo->m_env = ptinfo->m_env; } } else { // // Parent is an invalid thread, which is strange since it's performing // a clone. We try to remove and look it up in proc. // m_inspector->remove_thread(tid, true); tid_collision = true; ptinfo = m_inspector->get_thread(tid, true, true); if(ptinfo == NULL) { // // This can happen if the thread table has reached max capacity // ASSERT(false); return; } if(ptinfo->m_comm != "" && ptinfo->m_uid != 0xffffffff) { // // Parent found in proc, use its data // tinfo->m_comm = ptinfo->m_comm; tinfo->m_exe = ptinfo->m_exe; tinfo->m_exepath = ptinfo->m_exepath; tinfo->m_args = ptinfo->m_args; tinfo->m_root = ptinfo->m_root; tinfo->m_sid = ptinfo->m_sid; tinfo->m_vpgid = ptinfo->m_vpgid; tinfo->m_tty = ptinfo->m_tty; tinfo->m_loginuid = ptinfo->m_loginuid; if(!(flags & PPM_CL_CLONE_THREAD)) { tinfo->m_env = ptinfo->m_env; } } else { // // Parent not found in proc, use the event data. // (The session id will remain unset) // parinfo = evt->get_param(1); tinfo->m_exe = (char*)parinfo->m_val; switch(etype) { case PPME_SYSCALL_CLONE_11_X: case PPME_SYSCALL_CLONE_16_X: case PPME_SYSCALL_FORK_X: case PPME_SYSCALL_VFORK_X: tinfo->m_comm = tinfo->m_exe; break; case PPME_SYSCALL_CLONE_17_X: case PPME_SYSCALL_CLONE_20_X: case PPME_SYSCALL_FORK_17_X: case PPME_SYSCALL_FORK_20_X: case PPME_SYSCALL_VFORK_17_X: case PPME_SYSCALL_VFORK_20_X: parinfo = evt->get_param(13); tinfo->m_comm = parinfo->m_val; break; default: ASSERT(false); } parinfo = evt->get_param(2); tinfo->set_args(parinfo->m_val, parinfo->m_len); // // Also, propagate the same values to the parent // ptinfo->m_comm = tinfo->m_comm; ptinfo->m_exe = tinfo->m_exe; ptinfo->m_exepath = tinfo->m_exepath; ptinfo->set_args(parinfo->m_val, parinfo->m_len); } } // Copy the pid parinfo = evt->get_param(4); ASSERT(parinfo->m_len == sizeof(int64_t)); tinfo->m_pid = *(int64_t *)parinfo->m_val; // Get the flags, and check if this is a thread or a new thread tinfo->m_flags = flags; // // If clone()'s PPM_CL_CLONE_THREAD is not set it means that a new // thread was created. In that case, we set the pid to the one of the CHILD thread that // is going to be created. // if(!(tinfo->m_flags & PPM_CL_CLONE_THREAD)) { tinfo->m_pid = childtid; } if(!(tinfo->m_flags & PPM_CL_CLONE_THREAD)) { // // Copy the fd list // XXX this is a gross oversimplification that will need to be fixed. // What we do is: if the child is NOT a thread, we copy all the parent fds. // The right thing to do is looking at PPM_CL_CLONE_FILES, but there are // syscalls like open and pipe2 that can override PPM_CL_CLONE_FILES with the O_CLOEXEC flag // tinfo->m_fdtable = *(ptinfo->get_fd_table()); // // Track down that those are cloned fds // for(auto fdit = tinfo->m_fdtable.m_table.begin(); fdit != tinfo->m_fdtable.m_table.end(); ++fdit) { fdit->second.set_is_cloned(); } // // It's important to reset the cache of the child thread, to prevent it from // referring to an element in the parent's table. // tinfo->m_fdtable.reset_cache(); // // Not a thread, copy cwd // tinfo->m_cwd = ptinfo->get_cwd(); } //if((tinfo->m_flags & (PPM_CL_CLONE_FILES))) //{ // tinfo->m_fdtable = ptinfo.m_fdtable; //} if(is_inverted_clone) { tinfo->m_flags |= PPM_CL_CLONE_INVERTED; } // Copy the command name parinfo = evt->get_param(1); tinfo->m_exe = (char*)parinfo->m_val; switch(etype) { case PPME_SYSCALL_CLONE_11_X: case PPME_SYSCALL_CLONE_16_X: case PPME_SYSCALL_FORK_X: case PPME_SYSCALL_VFORK_X: tinfo->m_comm = tinfo->m_exe; break; case PPME_SYSCALL_CLONE_17_X: case PPME_SYSCALL_CLONE_20_X: case PPME_SYSCALL_FORK_17_X: case PPME_SYSCALL_FORK_20_X: case PPME_SYSCALL_VFORK_17_X: case PPME_SYSCALL_VFORK_20_X: parinfo = evt->get_param(13); tinfo->m_comm = parinfo->m_val; break; default: ASSERT(false); } // Get the command arguments parinfo = evt->get_param(2); tinfo->set_args(parinfo->m_val, parinfo->m_len); // Copy the fdlimit parinfo = evt->get_param(7); ASSERT(parinfo->m_len == sizeof(int64_t)); tinfo->m_fdlimit = *(int64_t *)parinfo->m_val; switch(etype) { case PPME_SYSCALL_CLONE_11_X: break; case PPME_SYSCALL_CLONE_16_X: case PPME_SYSCALL_CLONE_17_X: case PPME_SYSCALL_CLONE_20_X: case PPME_SYSCALL_FORK_X: case PPME_SYSCALL_FORK_17_X: case PPME_SYSCALL_FORK_20_X: case PPME_SYSCALL_VFORK_X: case PPME_SYSCALL_VFORK_17_X: case PPME_SYSCALL_VFORK_20_X: // Get the pgflt_maj parinfo = evt->get_param(8); ASSERT(parinfo->m_len == sizeof(uint64_t)); tinfo->m_pfmajor = *(uint64_t *)parinfo->m_val; // Get the pgflt_min parinfo = evt->get_param(9); ASSERT(parinfo->m_len == sizeof(uint64_t)); tinfo->m_pfminor = *(uint64_t *)parinfo->m_val; // Get the vm_size parinfo = evt->get_param(10); ASSERT(parinfo->m_len == sizeof(uint32_t)); tinfo->m_vmsize_kb = *(uint32_t *)parinfo->m_val; // Get the vm_rss parinfo = evt->get_param(11); ASSERT(parinfo->m_len == sizeof(uint32_t)); tinfo->m_vmrss_kb = *(uint32_t *)parinfo->m_val; // Get the vm_swap parinfo = evt->get_param(12); ASSERT(parinfo->m_len == sizeof(uint32_t)); tinfo->m_vmswap_kb = *(uint32_t *)parinfo->m_val; break; default: ASSERT(false); } // Copy the uid switch(etype) { case PPME_SYSCALL_CLONE_11_X: parinfo = evt->get_param(9); break; case PPME_SYSCALL_CLONE_16_X: case PPME_SYSCALL_FORK_X: case PPME_SYSCALL_VFORK_X: parinfo = evt->get_param(14); break; case PPME_SYSCALL_CLONE_17_X: case PPME_SYSCALL_FORK_17_X: case PPME_SYSCALL_VFORK_17_X: parinfo = evt->get_param(15); break; case PPME_SYSCALL_CLONE_20_X: case PPME_SYSCALL_FORK_20_X: case PPME_SYSCALL_VFORK_20_X: parinfo = evt->get_param(16); break; default: ASSERT(false); } ASSERT(parinfo->m_len == sizeof(int32_t)); tinfo->m_uid = *(int32_t *)parinfo->m_val; // Copy the gid switch(etype) { case PPME_SYSCALL_CLONE_11_X: parinfo = evt->get_param(10); break; case PPME_SYSCALL_CLONE_16_X: case PPME_SYSCALL_FORK_X: case PPME_SYSCALL_VFORK_X: parinfo = evt->get_param(15); break; case PPME_SYSCALL_CLONE_17_X: case PPME_SYSCALL_FORK_17_X: case PPME_SYSCALL_VFORK_17_X: parinfo = evt->get_param(16); break; case PPME_SYSCALL_CLONE_20_X: case PPME_SYSCALL_FORK_20_X: case PPME_SYSCALL_VFORK_20_X: parinfo = evt->get_param(17); break; default: ASSERT(false); } ASSERT(parinfo->m_len == sizeof(int32_t)); tinfo->m_gid = *(int32_t *)parinfo->m_val; // // If we're in a container, vtid and vpid are // initialized to the values coming from the event, // otherwise they are just set to tid and pid. We can't // use the event in that case because in a non-container // case also the clone exit from the father can create a // child process, and it doesn't have the right vtid and vpid // values // if(in_container) { tinfo->m_vtid = vtid; tinfo->m_vpid = vpid; } else { tinfo->m_vtid = tinfo->m_tid; tinfo->m_vpid = tinfo->m_pid; } // // Set cgroups and heuristically detect container id // switch(etype) { case PPME_SYSCALL_FORK_20_X: case PPME_SYSCALL_VFORK_20_X: case PPME_SYSCALL_CLONE_20_X: parinfo = evt->get_param(14); tinfo->set_cgroups(parinfo->m_val, parinfo->m_len); m_inspector->m_container_manager.resolve_container(tinfo, m_inspector->is_live()); break; } // // Initialize the thread clone time // tinfo->m_clone_ts = evt->get_ts(); // // Add the new thread to the table // bool thread_added = m_inspector->add_thread(tinfo); // // If there's a listener, invoke it // if(m_fd_listener) { m_fd_listener->on_clone(evt, tinfo); } // // If we had to erase a previous entry for this tid and rebalance the table, // make sure we reinitialize the tinfo pointer for this event, as the thread // generating it might have gone away. // if(tid_collision) { reset(evt); #ifdef HAS_ANALYZER m_inspector->m_tid_collisions.push_back(tinfo->m_tid); #endif DBG_SINSP_INFO("tid collision for %" PRIu64 "(%s)", tinfo->m_tid, tinfo->m_comm.c_str()); } if (!thread_added) { delete tinfo; } return; } void sinsp_parser::parse_execve_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; uint16_t etype = evt->get_type(); sinsp_evt *enter_evt = &m_tmp_evt; // Validate the return value parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); retval = *(int64_t *)parinfo->m_val; if(retval < 0) { return; } // // We get here when execve returns. The thread has already been added by a previous fork or clone, // and we just update the entry with the new information. // if(!evt->m_tinfo) { // // No thread to update? // We probably missed the start event, so we will just do nothing // //fprintf(stderr, "comm = %s, args = %s\n",evt->get_param(1)->m_val,evt->get_param(1)->m_val); //ASSERT(false); return; } // Get the exe parinfo = evt->get_param(1); evt->m_tinfo->m_exe = parinfo->m_val; switch(etype) { case PPME_SYSCALL_EXECVE_8_X: case PPME_SYSCALL_EXECVE_13_X: case PPME_SYSCALL_EXECVE_14_X: // Old trace files didn't have comm, so just set it to exe evt->m_tinfo->m_comm = evt->m_tinfo->m_exe; break; case PPME_SYSCALL_EXECVE_15_X: case PPME_SYSCALL_EXECVE_16_X: case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_19_X: // Get the comm parinfo = evt->get_param(13); evt->m_tinfo->m_comm = parinfo->m_val; break; default: ASSERT(false); } // Get the command arguments parinfo = evt->get_param(2); evt->m_tinfo->set_args(parinfo->m_val, parinfo->m_len); // Get the pid parinfo = evt->get_param(4); ASSERT(parinfo->m_len == sizeof(uint64_t)); evt->m_tinfo->m_pid = *(uint64_t *)parinfo->m_val; // // In case this thread is a fake entry, // try to at least patch the parent, since // we have it from the execve event // if(evt->m_tinfo->m_ptid == -1) { parinfo = evt->get_param(5); ASSERT(parinfo->m_len == sizeof(uint64_t)); evt->m_tinfo->m_ptid = *(uint64_t *)parinfo->m_val; } // Get the fdlimit parinfo = evt->get_param(7); ASSERT(parinfo->m_len == sizeof(int64_t)); evt->m_tinfo->m_fdlimit = *(int64_t *)parinfo->m_val; switch(etype) { case PPME_SYSCALL_EXECVE_8_X: break; case PPME_SYSCALL_EXECVE_13_X: case PPME_SYSCALL_EXECVE_14_X: case PPME_SYSCALL_EXECVE_15_X: case PPME_SYSCALL_EXECVE_16_X: case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_19_X: // Get the pgflt_maj parinfo = evt->get_param(8); ASSERT(parinfo->m_len == sizeof(uint64_t)); evt->m_tinfo->m_pfmajor = *(uint64_t *)parinfo->m_val; // Get the pgflt_min parinfo = evt->get_param(9); ASSERT(parinfo->m_len == sizeof(uint64_t)); evt->m_tinfo->m_pfminor = *(uint64_t *)parinfo->m_val; // Get the vm_size parinfo = evt->get_param(10); ASSERT(parinfo->m_len == sizeof(uint32_t)); evt->m_tinfo->m_vmsize_kb = *(uint32_t *)parinfo->m_val; // Get the vm_rss parinfo = evt->get_param(11); ASSERT(parinfo->m_len == sizeof(uint32_t)); evt->m_tinfo->m_vmrss_kb = *(uint32_t *)parinfo->m_val; // Get the vm_swap parinfo = evt->get_param(12); ASSERT(parinfo->m_len == sizeof(uint32_t)); evt->m_tinfo->m_vmswap_kb = *(uint32_t *)parinfo->m_val; break; default: ASSERT(false); } switch(etype) { case PPME_SYSCALL_EXECVE_8_X: case PPME_SYSCALL_EXECVE_13_X: break; case PPME_SYSCALL_EXECVE_14_X: // Get the environment parinfo = evt->get_param(13); evt->m_tinfo->set_env(parinfo->m_val, parinfo->m_len); break; case PPME_SYSCALL_EXECVE_15_X: // Get the environment parinfo = evt->get_param(14); evt->m_tinfo->set_env(parinfo->m_val, parinfo->m_len); break; case PPME_SYSCALL_EXECVE_16_X: case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_19_X: // Get the environment parinfo = evt->get_param(15); evt->m_tinfo->set_env(parinfo->m_val, parinfo->m_len); // // Set cgroups and heuristically detect container id // parinfo = evt->get_param(14); evt->m_tinfo->set_cgroups(parinfo->m_val, parinfo->m_len); // // Resync container status after an execve, we need to do it // because at container startup docker spawn a process with vpid=1 // outside of container cgroup and correct cgroups are // assigned just before doing execve: // // 1. docker-runc calls fork() and created process with vpid=1 // 2. docker-runc changes cgroup hierarchy of it // 3. vpid=1 execve to the real process the user wants to run inside the container // m_inspector->m_container_manager.resolve_container(evt->m_tinfo, m_inspector->is_live()); break; default: ASSERT(false); } switch(etype) { case PPME_SYSCALL_EXECVE_8_X: case PPME_SYSCALL_EXECVE_13_X: case PPME_SYSCALL_EXECVE_14_X: case PPME_SYSCALL_EXECVE_15_X: case PPME_SYSCALL_EXECVE_16_X: break; case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_19_X: // Get the tty parinfo = evt->get_param(16); ASSERT(parinfo->m_len == sizeof(int32_t)); evt->m_tinfo->m_tty = *(int32_t *) parinfo->m_val; break; default: ASSERT(false); } switch(etype) { case PPME_SYSCALL_EXECVE_8_X: case PPME_SYSCALL_EXECVE_13_X: case PPME_SYSCALL_EXECVE_14_X: case PPME_SYSCALL_EXECVE_15_X: case PPME_SYSCALL_EXECVE_16_X: case PPME_SYSCALL_EXECVE_17_X: break; case PPME_SYSCALL_EXECVE_18_X: case PPME_SYSCALL_EXECVE_19_X: // Get exepath if (retrieve_enter_event(enter_evt, evt)) { char fullpath[SCAP_MAX_PATH_SIZE]; parinfo = enter_evt->get_param(0); if (strncmp(parinfo->m_val, "", 4) == 0) { evt->m_tinfo->m_exepath = ""; } else { sinsp_utils::concatenate_paths(fullpath, SCAP_MAX_PATH_SIZE, evt->m_tinfo->m_cwd.c_str(), (uint32_t)evt->m_tinfo->m_cwd.size(), parinfo->m_val, (uint32_t)parinfo->m_len); evt->m_tinfo->m_exepath = fullpath; } } break; default: ASSERT(false); } switch(etype) { case PPME_SYSCALL_EXECVE_8_X: case PPME_SYSCALL_EXECVE_13_X: case PPME_SYSCALL_EXECVE_14_X: case PPME_SYSCALL_EXECVE_15_X: case PPME_SYSCALL_EXECVE_16_X: case PPME_SYSCALL_EXECVE_17_X: case PPME_SYSCALL_EXECVE_18_X: break; case PPME_SYSCALL_EXECVE_19_X: // Get the vpgid parinfo = evt->get_param(17); ASSERT(parinfo->m_len == sizeof(int64_t)); evt->m_tinfo->m_vpgid = *(int64_t *) parinfo->m_val; break; default: ASSERT(false); } // From scap version 1.2, event types of existent // events are no longer changed. // sinsp_evt::get_num_params() can instead be used // to identify the version of the event. // For example: // // if(evt->get_num_params() > 18) // { // ... // } // Get the loginuid if(evt->get_num_params() > 18) { parinfo = evt->get_param(18); ASSERT(parinfo->m_len == sizeof(uint32_t)); evt->m_tinfo->m_loginuid = *(uint32_t *) parinfo->m_val; } // // execve starts with a clean fd list, so we get rid of the fd list that clone // copied from the parent // XXX validate this // // scap_fd_free_table(handle, tinfo); // // Clear the flags for this thread, making sure to propagate the inverted // and shell pipe flags // auto spf = evt->m_tinfo->m_flags & (PPM_CL_PIPE_SRC | PPM_CL_PIPE_DST); bool inverted = ((evt->m_tinfo->m_flags & PPM_CL_CLONE_INVERTED) != 0); evt->m_tinfo->m_flags = PPM_CL_ACTIVE; evt->m_tinfo->m_flags |= spf; if(inverted) { evt->m_tinfo->m_flags |= PPM_CL_CLONE_INVERTED; } // // This process' name changed, so we need to include it in the protocol again // evt->m_tinfo->m_flags |= PPM_CL_NAME_CHANGED; // // Recompute the program hash // evt->m_tinfo->compute_program_hash(); #ifdef HAS_ANALYZER if(evt->m_tinfo->m_ainfo != NULL) { evt->m_tinfo->m_ainfo->clear_role_flags(); } #endif // // If there's a listener, invoke it // if(m_fd_listener) { m_fd_listener->on_execve(evt); } return; } void sinsp_parser::parse_openat_dir(sinsp_evt *evt, char* name, int64_t dirfd, OUT string* sdir) { bool is_absolute = (name[0] == '/'); string tdirstr; if(is_absolute) { // // The path is absolute. // Some processes (e.g. irqbalance) actually do this: they pass an invalid fd and // and absolute path, and openat succeeds. // *sdir = "."; } else if(dirfd == PPM_AT_FDCWD) { *sdir = evt->m_tinfo->get_cwd(); } else { evt->m_fdinfo = evt->m_tinfo->get_fd(dirfd); if(evt->m_fdinfo == NULL) { ASSERT(false); *sdir = ""; } else { if(evt->m_fdinfo->m_name[evt->m_fdinfo->m_name.length()] == '/') { *sdir = evt->m_fdinfo->m_name; } else { tdirstr = evt->m_fdinfo->m_name + '/'; *sdir = tdirstr; } } } } template void schedule_more_evts(sinsp* inspector, void* data, T* client, ppm_event_type evt_type) { #ifdef HAS_CAPTURE ASSERT(data); bool good_event = false; metaevents_state* state = (metaevents_state*)data; if(state->m_new_group == true) { state->m_new_group = false; inspector->add_meta_event(&state->m_metaevt); return; } ASSERT(client); if(!client->get_capture_events().size()) { SINSP_STR_ERROR( std::string("An event scheduled but no events available." "All pending event requests for " "[") + typeid(T).name() + "] are cancelled."); state->m_new_group = false; state->m_n_additional_events_to_add = 0; inspector->remove_meta_event_callback(); return; } string payload = client->dequeue_capture_event(); std::size_t tot_len = sizeof(scap_evt) + sizeof(uint16_t) + payload.size() + 1; if(tot_len > state->m_scap_buf_size) { sinsp_parser::init_scapevt(*state, evt_type, tot_len); } state->m_piscapevt->len = tot_len; state->m_piscapevt->nparams = 1; uint16_t* plen = (uint16_t*)((char *)state->m_piscapevt + sizeof(struct ppm_evt_hdr)); plen[0] = (uint16_t)payload.size() + 1; uint8_t* edata = (uint8_t*)plen + sizeof(uint16_t); memcpy(edata, payload.c_str(), plen[0]); good_event = true; state->m_n_additional_events_to_add--; if(state->m_n_additional_events_to_add == 0) { inspector->remove_meta_event_callback(); } else if(good_event) { inspector->add_meta_event(&state->m_metaevt); } #endif // HAS_CAPTURE } #ifndef CYGWING_AGENT void schedule_more_k8s_evts(sinsp* inspector, void* data) { schedule_more_evts(inspector, data, inspector->get_k8s_client(), PPME_K8S_E); } void sinsp_parser::schedule_k8s_events() { #ifdef HAS_CAPTURE // // schedule k8s events, if any available // k8s* k8s_client = 0; if(m_inspector && (k8s_client = m_inspector->m_k8s_client)) { int event_count = k8s_client->get_capture_events().size(); if(event_count) { m_k8s_metaevents_state.m_piscapevt->tid = 0; m_k8s_metaevents_state.m_piscapevt->ts = m_inspector->m_lastevent_ts; m_k8s_metaevents_state.m_new_group = true; m_k8s_metaevents_state.m_n_additional_events_to_add = event_count; m_inspector->add_meta_event_callback(&schedule_more_k8s_evts, &m_k8s_metaevents_state); schedule_more_k8s_evts(m_inspector, &m_k8s_metaevents_state); } } #endif // HAS_CAPTURE } void schedule_more_mesos_evts(sinsp* inspector, void* data) { schedule_more_evts(inspector, data, inspector->get_mesos_client(), PPME_MESOS_E); } void sinsp_parser::schedule_mesos_events() { #ifdef HAS_CAPTURE // // schedule mesos events, if any available // mesos* mesos_client = 0; if(m_inspector && (mesos_client = m_inspector->m_mesos_client)) { int event_count = mesos_client->get_capture_events().size(); if(event_count) { m_mesos_metaevents_state.m_piscapevt->tid = 0; m_mesos_metaevents_state.m_piscapevt->ts = m_inspector->m_lastevent_ts; m_mesos_metaevents_state.m_new_group = true; m_mesos_metaevents_state.m_n_additional_events_to_add = event_count; m_inspector->add_meta_event_callback(&schedule_more_mesos_evts, &m_mesos_metaevents_state); schedule_more_mesos_evts(m_inspector, &m_mesos_metaevents_state); } } #endif // HAS_CAPTURE } #endif // CYGWING_AGENT void sinsp_parser::parse_open_openat_creat_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t fd; char *name; uint32_t namelen; uint32_t flags; sinsp_fdinfo_t fdi; sinsp_evt *enter_evt = &m_tmp_evt; string sdir; uint16_t etype = evt->get_type(); uint32_t dev = 0; ASSERT(evt->m_tinfo); if(evt->m_tinfo == nullptr) { return; } if(etype != PPME_SYSCALL_OPENAT_2_X) { // // Load the enter event so we can access its arguments // if(!retrieve_enter_event(enter_evt, evt)) { return; } } // // Check the return value // parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); fd = *(int64_t *)parinfo->m_val; // // Parse the parameters, based on the event type // if(etype == PPME_SYSCALL_OPEN_X) { parinfo = evt->get_param(1); name = parinfo->m_val; namelen = parinfo->m_len; parinfo = evt->get_param(2); ASSERT(parinfo->m_len == sizeof(uint32_t)); flags = *(uint32_t *)parinfo->m_val; if(evt->get_num_params() > 4) { parinfo = evt->get_param(4); ASSERT(parinfo->m_len == sizeof(uint32_t)); dev = *(uint32_t *)parinfo->m_val; } sdir = evt->m_tinfo->get_cwd(); } else if(etype == PPME_SYSCALL_CREAT_X) { parinfo = evt->get_param(1); name = parinfo->m_val; namelen = parinfo->m_len; flags = 0; if(evt->get_num_params() > 3) { parinfo = evt->get_param(3); ASSERT(parinfo->m_len == sizeof(uint32_t)); dev = *(uint32_t *)parinfo->m_val; } sdir = evt->m_tinfo->get_cwd(); } else if(etype == PPME_SYSCALL_OPENAT_X) { parinfo = enter_evt->get_param(1); name = parinfo->m_val; namelen = parinfo->m_len; parinfo = enter_evt->get_param(2); ASSERT(parinfo->m_len == sizeof(uint32_t)); flags = *(uint32_t *)parinfo->m_val; parinfo = enter_evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); int64_t dirfd = *(int64_t *)parinfo->m_val; parse_openat_dir(evt, name, dirfd, &sdir); } else if(etype == PPME_SYSCALL_OPENAT_2_X) { parinfo = evt->get_param(2); name = parinfo->m_val; namelen = parinfo->m_len; parinfo = evt->get_param(3); ASSERT(parinfo->m_len == sizeof(uint32_t)); flags = *(uint32_t *)parinfo->m_val; parinfo = evt->get_param(1); ASSERT(parinfo->m_len == sizeof(int64_t)); int64_t dirfd = *(int64_t *)parinfo->m_val; if(evt->get_num_params() > 5) { parinfo = evt->get_param(5); ASSERT(parinfo->m_len == sizeof(uint32_t)); dev = *(uint32_t *)parinfo->m_val; } parse_openat_dir(evt, name, dirfd, &sdir); } else { ASSERT(false); return; } // XXX not implemented yet //parinfo = evt->get_param(2); //ASSERT(parinfo->m_len == sizeof(uint32_t)); //mode = *(uint32_t*)parinfo->m_val; char fullpath[SCAP_MAX_PATH_SIZE]; sinsp_utils::concatenate_paths(fullpath, SCAP_MAX_PATH_SIZE, sdir.c_str(), (uint32_t)sdir.length(), name, namelen); if(fd >= 0) { // // Populate the new fdi // if(flags & PPM_O_DIRECTORY) { fdi.m_type = SCAP_FD_DIRECTORY; } else { fdi.m_type = SCAP_FD_FILE_V2; } fdi.m_openflags = flags; fdi.m_dev = dev; fdi.add_filename(fullpath); // // If this is a user event fd, mark it with the proper flag // if(fdi.m_name == USER_EVT_DEVICE_NAME) { fdi.m_flags |= sinsp_fdinfo_t::FLAGS_IS_TRACER_FILE; } else { fdi.m_flags |= sinsp_fdinfo_t::FLAGS_IS_NOT_TRACER_FD; } // // Add the fd to the table. // evt->m_fdinfo = evt->m_tinfo->add_fd(fd, &fdi); // // Call the protocol decoder callbacks associated to this event // vector::iterator it; for(it = m_open_callbacks.begin(); it != m_open_callbacks.end(); ++it) { (*it)->on_event(evt, CT_OPEN); } } if(m_fd_listener && !(flags & PPM_O_DIRECTORY)) { m_fd_listener->on_file_open(evt, fullpath, flags); } } // // Helper function to allocate a socket fd, initialize it by parsing its parameters and add it to the fd table of the given thread. // inline void sinsp_parser::add_socket(sinsp_evt *evt, int64_t fd, uint32_t domain, uint32_t type, uint32_t protocol) { sinsp_fdinfo_t fdi; // // Populate the new fdi // memset(&(fdi.m_sockinfo.m_ipv4info), 0, sizeof(fdi.m_sockinfo.m_ipv4info)); fdi.m_type = SCAP_FD_UNKNOWN; fdi.m_sockinfo.m_ipv4info.m_fields.m_l4proto = SCAP_L4_UNKNOWN; if(domain == PPM_AF_UNIX) { fdi.m_type = SCAP_FD_UNIX_SOCK; } else if(domain == PPM_AF_INET || domain == PPM_AF_INET6) { fdi.m_type = (domain == PPM_AF_INET)? SCAP_FD_IPV4_SOCK : SCAP_FD_IPV6_SOCK; uint8_t l4proto = SCAP_L4_UNKNOWN; if(protocol == IPPROTO_TCP) { l4proto = (type == SOCK_RAW)? SCAP_L4_RAW : SCAP_L4_TCP; } else if(protocol == IPPROTO_UDP) { l4proto = (type == SOCK_RAW)? SCAP_L4_RAW : SCAP_L4_UDP; } else if(protocol == IPPROTO_IP) { // // XXX: we mask type because, starting from linux 2.6.27, type can be ORed with // SOCK_NONBLOCK and SOCK_CLOEXEC. We need to validate that byte masking is // acceptable // if((type & 0xff) == SOCK_STREAM) { l4proto = SCAP_L4_TCP; } else if((type & 0xff) == SOCK_DGRAM) { l4proto = SCAP_L4_UDP; } else { ASSERT(false); } } else if(protocol == IPPROTO_ICMP) { l4proto = (type == SOCK_RAW)? SCAP_L4_RAW : SCAP_L4_ICMP; } else if(protocol == IPPROTO_RAW) { l4proto = SCAP_L4_RAW; } if(domain == PPM_AF_INET) { fdi.m_sockinfo.m_ipv4info.m_fields.m_l4proto = l4proto; } else { memset(&(fdi.m_sockinfo.m_ipv6info), 0, sizeof(fdi.m_sockinfo.m_ipv6info)); fdi.m_sockinfo.m_ipv6info.m_fields.m_l4proto = l4proto; } } else if(domain == PPM_AF_NETLINK) { fdi.m_type = SCAP_FD_NETLINK; } else { if( domain != 10 && // IPv6 domain != 17) // AF_PACKET, used for packet capture { // // IPv6 will go here // ASSERT(false); } } if(fdi.m_type == SCAP_FD_UNKNOWN) { SINSP_STR_DEBUG("Unknown fd fd=" + to_string(fd) + " domain=" + to_string(domain) + " type=" + to_string(type) + " protocol=" + to_string(protocol) + " pid=" + to_string(evt->m_tinfo->m_pid) + " comm=" + evt->m_tinfo->m_comm); } #ifndef INCLUDE_UNKNOWN_SOCKET_FDS if(fdi.m_type == SCAP_FD_UNKNOWN) { return; } #endif // // Add the fd to the table. // evt->m_fdinfo = evt->m_tinfo->add_fd(fd, &fdi); } /** * If we receive a call to 'sendto()' and the event's m_fdinfo is nullptr, * then we likely missed the call to 'socket()' that created the file * descriptor. In that case, we'll guess that it's a SOCK_DGRAM/UDP socket * and create the fdinfo based on that. * * Preconditions: evt->m_fdinfo == nullptr and * evt->m_tinfo != nullptr * */ inline void sinsp_parser::infer_sendto_fdinfo(sinsp_evt* const evt) { if((evt->m_fdinfo != nullptr) || (evt->m_tinfo == nullptr)) { ASSERT(evt->m_fdinfo == nullptr); ASSERT(evt->m_tinfo != nullptr); return; } const uint32_t FILE_DESCRIPTOR_PARAM = 0; const uint32_t SOCKET_TUPLE_PARAM = 2; sinsp_evt_param* parinfo = nullptr; parinfo = evt->get_param(FILE_DESCRIPTOR_PARAM); ASSERT(parinfo->m_len == sizeof(int64_t)); ASSERT(evt->get_param_info(FILE_DESCRIPTOR_PARAM)->type == PT_FD); const int64_t fd = *((int64_t*) parinfo->m_val); if(fd < 0) { // Call to sendto() with an invalid file descriptor return; } parinfo = evt->get_param(SOCKET_TUPLE_PARAM); const char addr_family = *((char*) parinfo->m_val); if((addr_family == AF_INET) || (addr_family == AF_INET6)) { const uint32_t domain = (addr_family == AF_INET) ? PPM_AF_INET : PPM_AF_INET6; SINSP_DEBUG("Call to sendto() with fd=%d; missing socket() " "data. Adding socket %s/SOCK_DGRAM/IPPROTO_UDP " "for command '%s', pid %d", fd, (domain == PPM_AF_INET) ? "PPM_AF_INET" : "PPM_AF_INET6", evt->m_tinfo->get_comm().c_str(), evt->m_tinfo->m_pid); // Here we're assuming sendto() means SOCK_DGRAM/UDP, but it // can be used with TCP. We have no way to know for sure at // this point. add_socket(evt, fd, domain, SOCK_DGRAM, IPPROTO_UDP); } } void sinsp_parser::parse_socket_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t fd; uint32_t domain; uint32_t type; uint32_t protocol; sinsp_evt *enter_evt = &m_tmp_evt; // // NOTE: we don't check the return value of get_param() because we know the arguments we need are there. // XXX this extraction would be much faster if we parsed the event manually to extract the // parameters in one scan. We don't care too much because we assume that we get here // seldom enough that saving few tens of CPU cycles is not important. // parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); fd = *(int64_t *)parinfo->m_val; if(fd < 0) { // // socket() failed. Nothing to add to the table. // return; } if(evt->m_tinfo == nullptr) { return; } // // Load the enter event so we can access its arguments // if(!retrieve_enter_event(enter_evt, evt)) { return; } // // Extract the arguments // parinfo = enter_evt->get_param(0); ASSERT(parinfo->m_len == sizeof(uint32_t)); domain = *(uint32_t *)parinfo->m_val; parinfo = enter_evt->get_param(1); ASSERT(parinfo->m_len == sizeof(uint32_t)); type = *(uint32_t *)parinfo->m_val; parinfo = enter_evt->get_param(2); ASSERT(parinfo->m_len == sizeof(uint32_t)); protocol = *(uint32_t *)parinfo->m_val; // // Allocate a new fd descriptor, populate it and add it to the thread fd table // add_socket(evt, fd, domain, type, protocol); } void sinsp_parser::parse_bind_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; const char *parstr; uint8_t *packed_data; uint8_t family; if(evt->m_fdinfo == NULL) { return; } parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(uint64_t)); retval = *(int64_t*)parinfo->m_val; if(retval < 0) { return; } parinfo = evt->get_param(1); if(parinfo->m_len == 0) { // // No address, there's nothing we can really do with this. // This happens for socket types that we don't support, so we have the assertion // to make sure that this is not a type of socket that we support. // ASSERT(!(evt->m_fdinfo->is_unix_socket() || evt->m_fdinfo->is_ipv4_socket())); return; } packed_data = (uint8_t*)parinfo->m_val; family = *packed_data; // // Update the FD info with this tuple, assume that if port > 0, means that // the socket is used for listening // if(family == PPM_AF_INET) { uint32_t ip = *(uint32_t *)(packed_data + 1); uint16_t port = *(uint16_t *)(packed_data + 5); if(port > 0) { evt->m_fdinfo->m_type = SCAP_FD_IPV4_SERVSOCK; evt->m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_ip = ip; evt->m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_port = port; evt->m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_l4proto = evt->m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_l4proto; evt->m_fdinfo->set_role_server(); } } else if (family == PPM_AF_INET6) { uint8_t* ip = packed_data + 1; uint16_t port = *(uint16_t *)(packed_data + 17); if(port > 0) { if(sinsp_utils::is_ipv4_mapped_ipv6(ip)) { evt->m_fdinfo->m_type = SCAP_FD_IPV4_SERVSOCK; evt->m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_l4proto = evt->m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_l4proto; evt->m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_ip = *(uint32_t *)(packed_data + 13); evt->m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_port = port; } else { evt->m_fdinfo->m_type = SCAP_FD_IPV6_SERVSOCK; evt->m_fdinfo->m_sockinfo.m_ipv6serverinfo.m_port = port; memcpy(evt->m_fdinfo->m_sockinfo.m_ipv6serverinfo.m_ip.m_b, ip, sizeof(ipv6addr)); evt->m_fdinfo->m_sockinfo.m_ipv6serverinfo.m_l4proto = evt->m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_l4proto; } evt->m_fdinfo->set_role_server(); } } // // Update the name of this socket // evt->m_fdinfo->m_name = evt->get_param_as_str(1, &parstr, sinsp_evt::PF_SIMPLE); // // If there's a listener callback, invoke it // if(m_fd_listener) { m_fd_listener->on_bind(evt); } } void sinsp_parser::parse_connect_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; uint8_t *packed_data; uint8_t family; unordered_map::iterator fdit; const char *parstr; int64_t retval; bool changed; if(evt->m_fdinfo == NULL) { return; } parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(uint64_t)); retval = *(int64_t*)parinfo->m_val; if (m_track_connection_status) { if (retval == -SE_EINPROGRESS) { evt->m_fdinfo->set_socket_pending(); } else if(retval < 0) { evt->m_fdinfo->set_socket_failed(); } else { evt->m_fdinfo->set_socket_connected(); } } else { if (retval < 0 && retval != -SE_EINPROGRESS) { return; } else { evt->m_fdinfo->set_socket_connected(); } } parinfo = evt->get_param(1); if(parinfo->m_len == 0) { // // No address, there's nothing we can really do with this. // This happens for socket types that we don't support, so we have the assertion // to make sure that this is not a type of socket that we support. // ASSERT(!(evt->m_fdinfo->is_unix_socket() || evt->m_fdinfo->is_ipv4_socket())); return; } packed_data = (uint8_t*)parinfo->m_val; // // Validate the family // family = *packed_data; // // Fill the fd with the socket info // if(family == PPM_AF_INET || family == PPM_AF_INET6) { if(family == PPM_AF_INET6) { // // Check to see if it's an IPv4-mapped IPv6 address // (http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses) // uint8_t* sip = packed_data + 1; uint8_t* dip = packed_data + 19; if(!(sinsp_utils::is_ipv4_mapped_ipv6(sip) && sinsp_utils::is_ipv4_mapped_ipv6(dip))) { evt->m_fdinfo->m_type = SCAP_FD_IPV6_SOCK; changed = m_inspector->m_parser->set_ipv6_addresses_and_ports(evt->m_fdinfo, packed_data); } else { evt->m_fdinfo->m_type = SCAP_FD_IPV4_SOCK; changed = m_inspector->m_parser->set_ipv4_mapped_ipv6_addresses_and_ports(evt->m_fdinfo, packed_data); } } else { evt->m_fdinfo->m_type = SCAP_FD_IPV4_SOCK; // // Update the FD info with this tuple // changed = m_inspector->m_parser->set_ipv4_addresses_and_ports(evt->m_fdinfo, packed_data); } if(changed && evt->m_fdinfo->is_role_server() && evt->m_fdinfo->is_udp_socket()) { // connect done by a udp server, swap the addresses swap_addresses(evt->m_fdinfo); } // // Add the friendly name to the fd info // if(evt->m_fdinfo->is_role_server() && evt->m_fdinfo->is_udp_socket()) { sinsp_utils::sockinfo_to_str(&evt->m_fdinfo->m_sockinfo, evt->m_fdinfo->m_type, &evt->m_paramstr_storage[0], (uint32_t)evt->m_paramstr_storage.size(), m_inspector->m_hostname_and_port_resolution_enabled); evt->m_fdinfo->m_name = &evt->m_paramstr_storage[0]; } else { evt->m_fdinfo->m_name = evt->get_param_as_str(1, &parstr, sinsp_evt::PF_SIMPLE); } } else { if(!evt->m_fdinfo->is_unix_socket()) { // // This should happen only in case of a bug in our code, because I'm assuming that the OS // causes a connect with the wrong socket type to fail. // Assert in debug mode and just keep going in release mode. // ASSERT(false); } // // Add the friendly name to the fd info // evt->m_fdinfo->m_name = evt->get_param_as_str(1, &parstr, sinsp_evt::PF_SIMPLE); #ifndef HAS_ANALYZER // // Update the FD with this tuple // m_inspector->m_parser->set_unix_info(evt->m_fdinfo, packed_data); #endif } if(evt->m_fdinfo->is_role_none()) { // // Mark this fd as a client // evt->m_fdinfo->set_role_client(); } // // Call the protocol decoder callbacks associated to this event // vector::iterator it; for(it = m_connect_callbacks.begin(); it != m_connect_callbacks.end(); ++it) { (*it)->on_event(evt, CT_CONNECT); } // // If there's a listener callback, invoke it // if(m_fd_listener) { m_fd_listener->on_connect(evt, packed_data); } } void sinsp_parser::parse_accept_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t fd; uint8_t* packed_data; unordered_map::iterator fdit; sinsp_fdinfo_t fdi; const char *parstr; // // Lookup the thread // if(!evt->m_tinfo) { ASSERT(false); return; } // // Extract the fd // parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); fd = *(int64_t *)parinfo->m_val; if(fd < 0) { // // Accept failure. // Do nothing. // return; } // // Update the last event fd. It's needed by the filtering engine // evt->m_tinfo->m_lastevent_fd = fd; // // Extract the address // parinfo = evt->get_param(1); if(parinfo->m_len == 0) { // // No address, there's nothing we can really do with this. // This happens for socket types that we don't support, so we have the assertion // to make sure that this is not a type of socket that we support. // return; } packed_data = (uint8_t*)parinfo->m_val; // // Populate the fd info class // if(*packed_data == PPM_AF_INET) { set_ipv4_addresses_and_ports(&fdi, packed_data); fdi.m_type = SCAP_FD_IPV4_SOCK; fdi.m_sockinfo.m_ipv4info.m_fields.m_l4proto = SCAP_L4_TCP; } else if(*packed_data == PPM_AF_INET6) { // // Check to see if it's an IPv4-mapped IPv6 address // (http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses) // uint8_t* sip = packed_data + 1; uint8_t* dip = packed_data + 19; if(sinsp_utils::is_ipv4_mapped_ipv6(sip) && sinsp_utils::is_ipv4_mapped_ipv6(dip)) { set_ipv4_mapped_ipv6_addresses_and_ports(&fdi, packed_data); fdi.m_type = SCAP_FD_IPV4_SOCK; fdi.m_sockinfo.m_ipv4info.m_fields.m_l4proto = SCAP_L4_TCP; } else { set_ipv6_addresses_and_ports(&fdi, packed_data); fdi.m_type = SCAP_FD_IPV6_SOCK; fdi.m_sockinfo.m_ipv6info.m_fields.m_l4proto = SCAP_L4_TCP; } } else if(*packed_data == PPM_AF_UNIX) { fdi.m_type = SCAP_FD_UNIX_SOCK; set_unix_info(&fdi, packed_data); } else { // // Unsupported family // return; } fdi.m_name = evt->get_param_as_str(1, &parstr, sinsp_evt::PF_SIMPLE); fdi.m_flags = 0; if(m_fd_listener) { m_fd_listener->on_accept(evt, fd, packed_data, &fdi); } // // Mark this fd as a server // fdi.set_role_server(); // // Mark this fd as a connected socket // fdi.set_socket_connected(); // // Add the entry to the table // evt->m_fdinfo = evt->m_tinfo->add_fd(fd, &fdi); } void sinsp_parser::parse_close_enter(sinsp_evt *evt) { if(!evt->m_tinfo) { return; } evt->m_fdinfo = evt->m_tinfo->get_fd(evt->m_tinfo->m_lastevent_fd); if(evt->m_fdinfo == NULL) { return; } evt->m_fdinfo->m_flags |= sinsp_fdinfo_t::FLAGS_CLOSE_IN_PROGRESS; } // // This function takes care of cleaning up the FD and removing it from all the tables // (process FD table, connection table...). // It's invoked when a close() or a thread exit happens. // void sinsp_parser::erase_fd(erase_fd_params* params) { if(params->m_fdinfo == NULL) { // // This happens when more than one close has been canceled at the same time for // this thread. Since we currently handle just one canceling at at time (we // don't have a list of canceled closes, just a single entry), the second one // will generate a failed FD lookup. We do nothing. // NOTE: I do realize that this can cause a connection leak, I just assume that it's // rare enough that the delayed connection cleanup (when the timestamp expires) // is acceptable. // ASSERT(params->m_fd == CANCELED_FD_NUMBER); return; } // // Schedule the fd for removal // if(params->m_remove_from_table) { params->m_inspector->m_tid_of_fd_to_remove = params->m_tinfo->m_tid; params->m_inspector->m_fds_to_remove->push_back(params->m_fd); } if(m_fd_listener) { m_fd_listener->on_erase_fd(params); } } void sinsp_parser::parse_close_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); // // If the close() was successful, do the cleanup // if(retval >= 0) { if(evt->m_fdinfo == NULL || evt->m_tinfo == nullptr) { return; } // // a close gets canceled when the same fd is created successfully between // close enter and close exit. // erase_fd_params eparams; if(evt->m_fdinfo->m_flags & sinsp_fdinfo_t::FLAGS_CLOSE_CANCELED) { evt->m_fdinfo->m_flags &= ~sinsp_fdinfo_t::FLAGS_CLOSE_CANCELED; eparams.m_fd = CANCELED_FD_NUMBER; eparams.m_fdinfo = evt->m_tinfo->get_fd(CANCELED_FD_NUMBER); } else { eparams.m_fd = evt->m_tinfo->m_lastevent_fd; eparams.m_fdinfo = evt->m_fdinfo; } // // Remove the fd from the different tables // eparams.m_remove_from_table = true; eparams.m_inspector = m_inspector; eparams.m_tinfo = evt->m_tinfo; eparams.m_ts = evt->get_ts(); erase_fd(&eparams); } else { if(evt->m_fdinfo != NULL) { evt->m_fdinfo->m_flags &= ~sinsp_fdinfo_t::FLAGS_CLOSE_IN_PROGRESS; } // // It is normal when a close fails that the fd lookup failed, so we revert the // increment of m_n_failed_fd_lookups (for the enter event too if there's one). // #ifdef GATHER_INTERNAL_STATS m_inspector->m_stats.m_n_failed_fd_lookups--; #endif if(evt->m_tinfo && evt->m_tinfo->is_lastevent_data_valid()) { #ifdef GATHER_INTERNAL_STATS m_inspector->m_stats.m_n_failed_fd_lookups--; #endif } } } void sinsp_parser::add_pipe(sinsp_evt *evt, int64_t tid, int64_t fd, uint64_t ino) { sinsp_fdinfo_t fdi; // // lookup the thread info // if(!evt->m_tinfo) { return; } // // Populate the new fdi // fdi.m_type = SCAP_FD_FIFO; fdi.m_name = ""; fdi.m_ino = ino; // // Add the fd to the table. // evt->m_fdinfo = evt->m_tinfo->add_fd(fd, &fdi); } void sinsp_parser::parse_socketpair_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t fd1, fd2; int64_t retval; uint64_t source_address; uint64_t peer_address; parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); if(retval < 0) { // // socketpair() failed. Nothing to add to the table. // return; } if(evt->m_tinfo == nullptr) { // There is nothing we can do here if tinfo is missing return; } parinfo = evt->get_param(1); ASSERT(parinfo->m_len == sizeof(int64_t)); fd1 = *(int64_t *)parinfo->m_val; parinfo = evt->get_param(2); ASSERT(parinfo->m_len == sizeof(int64_t)); fd2 = *(int64_t *)parinfo->m_val; parinfo = evt->get_param(3); ASSERT(parinfo->m_len == sizeof(uint64_t)); source_address = *(uint64_t *)parinfo->m_val; parinfo = evt->get_param(4); ASSERT(parinfo->m_len == sizeof(uint64_t)); peer_address = *(uint64_t *)parinfo->m_val; sinsp_fdinfo_t fdi; fdi.m_type = SCAP_FD_UNIX_SOCK; fdi.m_sockinfo.m_unixinfo.m_fields.m_source = source_address; fdi.m_sockinfo.m_unixinfo.m_fields.m_dest = peer_address; evt->m_fdinfo = evt->m_tinfo->add_fd(fd1, &fdi); evt->m_tinfo->add_fd(fd2, &fdi); } void sinsp_parser::parse_pipe_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t fd1, fd2; int64_t retval; uint64_t ino; parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); if(retval < 0) { // // pipe() failed. Nothing to add to the table. // return; } parinfo = evt->get_param(1); ASSERT(parinfo->m_len == sizeof(int64_t)); fd1 = *(int64_t *)parinfo->m_val; parinfo = evt->get_param(2); ASSERT(parinfo->m_len == sizeof(int64_t)); fd2 = *(int64_t *)parinfo->m_val; parinfo = evt->get_param(3); ASSERT(parinfo->m_len == sizeof(uint64_t)); ino = *(uint64_t *)parinfo->m_val; add_pipe(evt, evt->get_tid(), fd1, ino); add_pipe(evt, evt->get_tid(), fd2, ino); } void sinsp_parser::parse_thread_exit(sinsp_evt *evt) { // // Schedule the process for removal // if(evt->m_tinfo) { evt->m_tinfo->m_flags |= PPM_CL_CLOSED; m_inspector->m_tid_to_remove = evt->get_tid(); } } bool sinsp_parser::set_ipv4_addresses_and_ports(sinsp_fdinfo_t* fdinfo, uint8_t* packed_data) { uint32_t tsip, tdip; uint16_t tsport, tdport; tsip = *(uint32_t *)(packed_data + 1); tsport = *(uint16_t *)(packed_data + 5); tdip = *(uint32_t *)(packed_data + 7); tdport = *(uint16_t *)(packed_data + 11); if(fdinfo->m_type == SCAP_FD_IPV4_SOCK) { if((tsip == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip && tsport == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport && tdip == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip && tdport == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport) || (tdip == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip && tdport == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport && tsip == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip && tsport == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport) ) { return false; } } fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip = tsip; fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport = tsport; fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip = tdip; fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport = tdport; return true; } bool sinsp_parser::set_ipv4_mapped_ipv6_addresses_and_ports(sinsp_fdinfo_t* fdinfo, uint8_t* packed_data) { uint32_t tsip, tdip; uint16_t tsport, tdport; tsip = *(uint32_t *)(packed_data + 13); tsport = *(uint16_t *)(packed_data + 17); tdip = *(uint32_t *)(packed_data + 31); tdport = *(uint16_t *)(packed_data + 35); if(fdinfo->m_type == SCAP_FD_IPV4_SOCK) { if((tsip == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip && tsport == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport && tdip == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip && tdport == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport) || (tdip == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip && tdport == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport && tsip == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip && tsport == fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport) ) { return false; } } fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip = tsip; fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport = tsport; fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip = tdip; fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport = tdport; return true; } bool sinsp_parser::set_ipv6_addresses_and_ports(sinsp_fdinfo_t* fdinfo, uint8_t* packed_data) { ipv6addr tsip, tdip; uint16_t tsport, tdport; memcpy((uint8_t *) tsip.m_b, packed_data + 1, sizeof(tsip.m_b)); tsport = *(uint16_t *)(packed_data + 17); memcpy((uint8_t *) tdip.m_b, packed_data + 19, sizeof(tdip.m_b)); tdport = *(uint16_t *)(packed_data + 35); if(fdinfo->m_type == SCAP_FD_IPV6_SOCK) { if((tsip == fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip && tsport == fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sport && tdip == fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip && tdport == fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport) || (tdip == fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip && tdport == fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sport && tsip == fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip && tsport == fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport) ) { return false; } } fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip = tsip; fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sport = tsport; fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip = tdip; fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport = tdport; return true; } bool sinsp_parser::set_unix_info(sinsp_fdinfo_t* fdinfo, uint8_t* packed_data) { fdinfo->m_sockinfo.m_unixinfo.m_fields.m_source = *(uint64_t *)(packed_data + 1); fdinfo->m_sockinfo.m_unixinfo.m_fields.m_dest = *(uint64_t *)(packed_data + 9); return true; } // Return false if the update didn't happen (for example because the tuple is NULL) bool sinsp_parser::update_fd(sinsp_evt *evt, sinsp_evt_param *parinfo) { uint8_t* packed_data = (uint8_t*)parinfo->m_val; uint8_t family = *packed_data; if(parinfo->m_len == 0) { return false; } if(family == PPM_AF_INET) { if(evt->m_fdinfo->m_type == SCAP_FD_IPV4_SERVSOCK) { // // If this was previously a server socket, propagate the L4 protocol // evt->m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_l4proto = evt->m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_l4proto; } evt->m_fdinfo->m_type = SCAP_FD_IPV4_SOCK; if(set_ipv4_addresses_and_ports(evt->m_fdinfo, packed_data) == false) { return false; } } else if(family == PPM_AF_INET6) { // // Check to see if it's an IPv4-mapped IPv6 address // (http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses) // uint8_t* sip = packed_data + 1; uint8_t* dip = packed_data + 19; if(sinsp_utils::is_ipv4_mapped_ipv6(sip) && sinsp_utils::is_ipv4_mapped_ipv6(dip)) { evt->m_fdinfo->m_type = SCAP_FD_IPV4_SOCK; if(set_ipv4_mapped_ipv6_addresses_and_ports(evt->m_fdinfo, packed_data) == false) { return false; } } else { // It's not an ipv4-mapped ipv6 address. Extract it as a normal address. if(set_ipv6_addresses_and_ports(evt->m_fdinfo, packed_data) == false) { return false; } } } else if(family == PPM_AF_UNIX) { evt->m_fdinfo->m_type = SCAP_FD_UNIX_SOCK; if(set_unix_info(evt->m_fdinfo, packed_data) == false) { return false; } evt->m_fdinfo->m_name = ((char*)packed_data) + 17; // // Call the protocol decoder callbacks to notify the decoders that this FD // changed. // vector::iterator it; for(it = m_connect_callbacks.begin(); it != m_connect_callbacks.end(); ++it) { (*it)->on_event(evt, CT_TUPLE_CHANGE); } return true; } // // If we reach this point and the protocol is not set yet, we assume this // connection is UDP, because TCP would fail if the address is changed in // the middle of a connection. // if(evt->m_fdinfo->m_type == SCAP_FD_IPV4_SOCK) { if(evt->m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_l4proto == SCAP_L4_UNKNOWN) { evt->m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_l4proto = SCAP_L4_UDP; } } else if(evt->m_fdinfo->m_type == SCAP_FD_IPV6_SOCK) { if(evt->m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_l4proto == SCAP_L4_UNKNOWN) { evt->m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_l4proto = SCAP_L4_UDP; } } // // If this is an incomplete tuple, patch it using interface info // m_inspector->m_network_interfaces->update_fd(evt->m_fdinfo); // // Call the protocol decoder callbacks to notify the decoders that this FD // changed. // vector::iterator it; for(it = m_connect_callbacks.begin(); it != m_connect_callbacks.end(); ++it) { (*it)->on_event(evt, CT_TUPLE_CHANGE); } return true; } void sinsp_parser::swap_addresses(sinsp_fdinfo_t* fdinfo) { if(fdinfo->m_type == SCAP_FD_IPV4_SOCK) { uint32_t tip; uint16_t tport; tip = fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip; tport = fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport; fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip = fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip; fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport = fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport; fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip = tip; fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport = tport; } else { ipv6addr tip; uint16_t tport; tip = fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip; tport = fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sport; fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sip = fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip;; fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sport = fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport; fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dip = tip; fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport = tport; } } uint32_t sinsp_parser::parse_tracer(sinsp_evt *evt, int64_t retval) { sinsp_threadinfo* tinfo = evt->m_tinfo; ASSERT(tinfo); // // Extract the data buffer // sinsp_evt_param *parinfo = evt->get_param(1); char* data = parinfo->m_val; uint32_t datalen = parinfo->m_len; sinsp_tracerparser* p = tinfo->m_tracer_parser; if(p == NULL) { p = tinfo->m_tracer_parser = new sinsp_tracerparser(m_inspector); } p->m_tinfo = tinfo; p->process_event_data(data, datalen, evt->get_ts()); if(p->m_res == sinsp_tracerparser::RES_TRUNCATED) { if(!m_inspector->m_is_dumping) { evt->m_filtered_out = true; } return p->m_res; } p->m_args.first = &p->m_argnames; p->m_args.second = &p->m_argvals; // // Populate the user event that we will send up the stack instead of the write // uint8_t* fakeevt_storage = (uint8_t*)m_fake_userevt; m_fake_userevt->ts = evt->m_pevt->ts; m_fake_userevt->tid = evt->m_pevt->tid; m_fake_userevt->nparams = 3; if(p->m_res == sinsp_tracerparser::RES_OK) { if(p->m_type_str[0] == '>') { m_fake_userevt->type = PPME_TRACER_E; } else { m_fake_userevt->type = PPME_TRACER_X; } uint16_t *lens = (uint16_t *)(fakeevt_storage + sizeof(struct ppm_evt_hdr)); lens[0] = 8; lens[1] = 8; lens[2] = 8; *(uint64_t *)(fakeevt_storage + sizeof(struct ppm_evt_hdr) + 6) = p->m_id; *(uint64_t *)(fakeevt_storage + sizeof(struct ppm_evt_hdr) + 14) = (uint64_t)&p->m_tags; *(uint64_t *)(fakeevt_storage + sizeof(struct ppm_evt_hdr) + 22) = (uint64_t)&p->m_args; } else { uint32_t flags = evt->m_fdinfo->m_flags; if(!(flags & sinsp_fdinfo_t::FLAGS_IS_TRACER_FD)) { return p->m_res; } // // Parsing error. // We don't know the direction, so we use enter. // p->m_argnames.clear(); p->m_argvals.clear(); m_fake_userevt->type = PPME_TRACER_E; uint16_t *lens = (uint16_t *)(fakeevt_storage + sizeof(struct ppm_evt_hdr)); lens[0] = 8; lens[1] = 8; lens[2] = 8; p->m_tags.clear(); m_tracer_error_string = "invalid tracer " + string(data, datalen) + ", len" + to_string(datalen); p->m_tags.push_back((char*)m_tracer_error_string.c_str()); *(uint64_t *)(fakeevt_storage + sizeof(struct ppm_evt_hdr) + 6) = 0; *(uint64_t *)(fakeevt_storage + sizeof(struct ppm_evt_hdr) + 14) = (uint64_t)&p->m_tags; *(uint64_t *)(fakeevt_storage + sizeof(struct ppm_evt_hdr) + 22) = (uint64_t)&p->m_args; } scap_evt* tevt = evt->m_pevt; evt->m_pevt = m_fake_userevt; evt->init(); evt->m_poriginal_evt = tevt; evt->m_flags |= (uint32_t)sinsp_evt::SINSP_EF_IS_TRACER; // // Update some thread information // tinfo->m_lastevent_fd = -1; tinfo->m_lastevent_type = PPME_TRACER_E; tinfo->m_latency = 0; tinfo->m_last_latency_entertime = 0; return p->m_res; } bool sinsp_parser::detect_and_process_tracer_write(sinsp_evt *evt, int64_t retval, ppm_event_flags eflags) { // // Tracers get into the engine as normal writes, but the FD has a flag to // quickly recognize them. // uint32_t flags = evt->m_fdinfo->m_flags; if(!(flags & sinsp_fdinfo_t::FLAGS_IS_NOT_TRACER_FD)) { sinsp_fdinfo_t* orifdinfo = evt->m_fdinfo; if(orifdinfo->m_flags & sinsp_fdinfo_t::FLAGS_IS_TRACER_FD) { parse_tracer(evt, retval); return true; } else { if(orifdinfo->m_flags & sinsp_fdinfo_t::FLAGS_IS_TRACER_FILE) { if(eflags & EF_WRITES_TO_FD) { // // We have not determined if this FD is a tracer FD or not. // We're going to try to parse it. // If the parsing succeeds, we mark it as a tracer FD. If it // fails we mark it an NOT a tracer FD. Otherwise, we wait // for the next buffer and we'll try again. // sinsp_tracerparser::parse_result pres = (sinsp_tracerparser::parse_result)parse_tracer(evt, retval); if(pres == sinsp_tracerparser::RES_OK) { // // This FD has been recognized to be a tracer one. // We do two things: mark it for future reference, and tell // the driver to enable tracers capture (if we haven't done // it yet). // orifdinfo->m_flags |= sinsp_fdinfo_t::FLAGS_IS_TRACER_FD; m_inspector->enable_tracers_capture(); return true; } else if (pres == sinsp_tracerparser::RES_FAILED) { orifdinfo->m_flags |= sinsp_fdinfo_t::FLAGS_IS_NOT_TRACER_FD; } } } } } return false; } void sinsp_parser::parse_rw_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; int64_t tid = evt->get_tid(); sinsp_evt *enter_evt = &m_tmp_evt; ppm_event_flags eflags = evt->get_info_flags(); // // Extract the return value // parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); retval = *(int64_t *)parinfo->m_val; if(evt->m_fdinfo == NULL) { if(!m_inspector->is_live()) { if((evt->get_dump_flags() & SCAP_DF_TRACER) != 0) { parse_tracer(evt, retval); return; } } return; } // // Check if this is a tracer write on /dev/null, treat it in a special way // if(detect_and_process_tracer_write(evt, retval, eflags)) { return; } // // If the operation was successful, validate that the fd exists // if(retval >= 0) { uint16_t etype = evt->get_type(); if (evt->m_fdinfo->m_type == SCAP_FD_IPV4_SOCK || evt->m_fdinfo->m_type == SCAP_FD_IPV6_SOCK) { evt->m_fdinfo->set_socket_connected(); } if(eflags & EF_READS_FROM_FD) { char *data; uint32_t datalen; int32_t tupleparam = -1; if(etype == PPME_SOCKET_RECVFROM_X) { tupleparam = 2; } else if(etype == PPME_SOCKET_RECVMSG_X) { tupleparam = 3; } if(tupleparam != -1 && (evt->m_fdinfo->m_name.length() == 0 || !evt->m_fdinfo->is_tcp_socket())) { // // recvfrom contains tuple info. // If the fd still doesn't contain tuple info (because the socket is a // datagram one or because some event was lost), // add it here. // if(update_fd(evt, evt->get_param(tupleparam))) { const char *parstr; scap_fd_type fdtype = evt->m_fdinfo->m_type; if(fdtype == SCAP_FD_IPV4_SOCK || fdtype == SCAP_FD_IPV6_SOCK) { if(evt->m_fdinfo->is_role_none()) { evt->m_fdinfo->set_net_role_by_guessing(m_inspector, evt->m_tinfo, evt->m_fdinfo, true); } if(evt->m_fdinfo->is_role_client()) { swap_addresses(evt->m_fdinfo); } sinsp_utils::sockinfo_to_str(&evt->m_fdinfo->m_sockinfo, fdtype, &evt->m_paramstr_storage[0], (uint32_t)evt->m_paramstr_storage.size(), m_inspector->m_hostname_and_port_resolution_enabled); evt->m_fdinfo->m_name = &evt->m_paramstr_storage[0]; } else { evt->m_fdinfo->m_name = evt->get_param_as_str(tupleparam, &parstr, sinsp_evt::PF_SIMPLE); } } } // // Extract the data buffer // if(etype == PPME_SYSCALL_READV_X || etype == PPME_SYSCALL_PREADV_X || etype == PPME_SOCKET_RECVMSG_X) { parinfo = evt->get_param(2); } else { parinfo = evt->get_param(1); } datalen = parinfo->m_len; data = parinfo->m_val; // // If there's an fd listener, call it now // if(m_fd_listener) { m_fd_listener->on_read(evt, tid, evt->m_tinfo->m_lastevent_fd, evt->m_fdinfo, data, (uint32_t)retval, datalen); } // // Call the protocol decoder callbacks associated to this event // if(evt->m_fdinfo->m_callbaks) { vector* cbacks = &(evt->m_fdinfo->m_callbaks->m_read_callbacks); for(auto it = cbacks->begin(); it != cbacks->end(); ++it) { (*it)->on_read(evt, data, datalen); } } } else { char *data; uint32_t datalen; int32_t tupleparam = -1; if(etype == PPME_SOCKET_SENDTO_X || etype == PPME_SOCKET_SENDMSG_X) { tupleparam = 2; } if(tupleparam != -1 && (evt->m_fdinfo->m_name.length() == 0 || !evt->m_fdinfo->is_tcp_socket())) { // // sendto contains tuple info in the enter event. // If the fd still doesn't contain tuple info (because the socket is a datagram one or because some event was lost), // add it here. // if(!retrieve_enter_event(enter_evt, evt)) { return; } if(update_fd(evt, enter_evt->get_param(tupleparam))) { const char *parstr; scap_fd_type fdtype = evt->m_fdinfo->m_type; if(fdtype == SCAP_FD_IPV4_SOCK || fdtype == SCAP_FD_IPV6_SOCK) { if(evt->m_fdinfo->is_role_none()) { evt->m_fdinfo->set_net_role_by_guessing(m_inspector, evt->m_tinfo, evt->m_fdinfo, false); } if(evt->m_fdinfo->is_role_server()) { swap_addresses(evt->m_fdinfo); } sinsp_utils::sockinfo_to_str(&evt->m_fdinfo->m_sockinfo, fdtype, &evt->m_paramstr_storage[0], (uint32_t)evt->m_paramstr_storage.size(), m_inspector->m_hostname_and_port_resolution_enabled); evt->m_fdinfo->m_name = &evt->m_paramstr_storage[0]; } else { evt->m_fdinfo->m_name = enter_evt->get_param_as_str(tupleparam, &parstr, sinsp_evt::PF_SIMPLE); } } } // // Extract the data buffer // parinfo = evt->get_param(1); datalen = parinfo->m_len; data = parinfo->m_val; // // If there's an fd listener, call it now // if(m_fd_listener) { m_fd_listener->on_write(evt, tid, evt->m_tinfo->m_lastevent_fd, evt->m_fdinfo, data, (uint32_t)retval, datalen); } // // Call the protocol decoder callbacks associated to this event // if(evt->m_fdinfo->m_callbaks) { vector* cbacks = &(evt->m_fdinfo->m_callbaks->m_write_callbacks); for(auto it = cbacks->begin(); it != cbacks->end(); ++it) { (*it)->on_write(evt, data, datalen); } } } } else if (m_track_connection_status) { if (evt->m_fdinfo->m_type == SCAP_FD_IPV4_SOCK || evt->m_fdinfo->m_type == SCAP_FD_IPV6_SOCK) { evt->m_fdinfo->set_socket_failed(); if (m_fd_listener) { m_fd_listener->on_socket_status_changed(evt); } } } } void sinsp_parser::parse_sendfile_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; if(!evt->m_fdinfo) { return; } // // Extract the return value // parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); retval = *(int64_t *)parinfo->m_val; // // If the operation was successful, validate that the fd exists // if(retval >= 0) { sinsp_evt *enter_evt = &m_tmp_evt; int64_t fdin; if(!retrieve_enter_event(enter_evt, evt)) { return; } // // Extract the in FD // parinfo = enter_evt->get_param(1); ASSERT(parinfo->m_len == sizeof(int64_t)); fdin = *(int64_t *)parinfo->m_val; // // If there's an fd listener, call it now // if(m_fd_listener) { m_fd_listener->on_sendfile(evt, fdin, (uint32_t)retval); } } } void sinsp_parser::parse_eventfd_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t fd; sinsp_fdinfo_t fdi; // // lookup the thread info // if(!evt->m_tinfo) { ASSERT(false); return; } parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); fd = *(int64_t *)parinfo->m_val; if(fd < 0) { // // eventfd() failed. Nothing to add to the table. // return; } // // Populate the new fdi // fdi.m_type = SCAP_FD_EVENT; fdi.m_name = ""; // // Add the fd to the table. // evt->m_fdinfo = evt->m_tinfo->add_fd(fd, &fdi); } void sinsp_parser::parse_chdir_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; if(!evt->m_tinfo) { return; } // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); // // In case of success, update the thread working dir // if(retval >= 0) { sinsp_evt_param *parinfo; // Update the thread working directory parinfo = evt->get_param(1); evt->m_tinfo->set_cwd(parinfo->m_val, parinfo->m_len); } } void sinsp_parser::parse_fchdir_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); // // In case of success, update the thread working dir // if(retval >= 0) { // // Find the fd name // if(evt->m_fdinfo == NULL || evt->m_tinfo == nullptr) { return; } // Update the thread working directory evt->m_tinfo->set_cwd((char *)evt->m_fdinfo->m_name.c_str(), (uint32_t)evt->m_fdinfo->m_name.size()); } } void sinsp_parser::parse_getcwd_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); // // Check if the syscall was successful // if(retval >= 0) { if(!evt->m_tinfo) { // // No thread in the table. We won't store this event, which mean that // we won't be able to parse the corresponding exit event and we'll have // to drop the information it carries. // ASSERT(false); return; } parinfo = evt->get_param(1); #ifdef _DEBUG string chkstr = string(parinfo->m_val); if(chkstr != "/") { if(chkstr + "/" != evt->m_tinfo->get_cwd()) { // // This shouldn't happen, because we should be able to stay in synch by // following chdir(). If it does, it's almost sure there was an event drop. // In that case, we use this value to update the thread cwd. // #if defined(HAS_CAPTURE) #ifdef _DEBUG int target_res; char target_name[1024]; target_res = readlink((chkstr + "/").c_str(), target_name, sizeof(target_name) - 1); if(target_res > 0) { if(target_name != evt->m_tinfo->get_cwd()) { printf("%s != %s", target_name, evt->m_tinfo->get_cwd().c_str()); ASSERT(false); } } #endif #endif } } #endif evt->m_tinfo->set_cwd(parinfo->m_val, parinfo->m_len); } } void sinsp_parser::parse_shutdown_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; // // Extract the return value // parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); retval = *(int64_t *)parinfo->m_val; // // If the operation was successful, do the cleanup // if(retval >= 0) { if(evt->m_fdinfo == NULL) { return; } if(m_fd_listener) { m_fd_listener->on_socket_shutdown(evt); } } } void sinsp_parser::parse_dup_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; if(evt->m_tinfo == nullptr) { return; } // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); // // Check if the syscall was successful // if(retval >= 0) { // // Heuristic to determine if a thread is part of a shell pipe // if(retval == 0) { evt->m_tinfo->m_flags |= PPM_CL_PIPE_DST; } if(retval == 1) { evt->m_tinfo->m_flags |= PPM_CL_PIPE_SRC; } if(evt->m_fdinfo == NULL) { return; } // // If the old FD is in the table, remove it properly // sinsp_fdinfo_t* oldfdinfo = evt->m_tinfo->get_fd(retval); if(oldfdinfo != NULL) { erase_fd_params eparams; eparams.m_fd = retval; eparams.m_fdinfo = oldfdinfo; eparams.m_remove_from_table = false; eparams.m_inspector = m_inspector; eparams.m_tinfo = evt->m_tinfo; eparams.m_ts = evt->get_ts(); erase_fd(&eparams); } // // Add the new fd to the table. // evt->m_fdinfo = evt->m_tinfo->add_fd(retval, evt->m_fdinfo); } } void sinsp_parser::parse_signalfd_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); if(evt->m_tinfo == nullptr) { return; } // // Check if the syscall was successful // if(retval >= 0) { sinsp_fdinfo_t fdi; // // Populate the new fdi // fdi.m_type = SCAP_FD_SIGNALFD; fdi.m_name = ""; // // Add the fd to the table. // evt->m_fdinfo = evt->m_tinfo->add_fd(retval, &fdi); } } void sinsp_parser::parse_timerfd_create_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); if(evt->m_tinfo == nullptr) { return; } // // Check if the syscall was successful // if(retval >= 0) { sinsp_fdinfo_t fdi; // // Populate the new fdi // fdi.m_type = SCAP_FD_TIMERFD; fdi.m_name = ""; // // Add the fd to the table. // evt->m_fdinfo = evt->m_tinfo->add_fd(retval, &fdi); } } void sinsp_parser::parse_inotify_init_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); if(evt->m_tinfo == nullptr) { return; } // // Check if the syscall was successful // if(retval >= 0) { sinsp_fdinfo_t fdi; // // Populate the new fdi // fdi.m_type = SCAP_FD_INOTIFY; fdi.m_name = ""; // // Add the fd to the table. // evt->m_fdinfo = evt->m_tinfo->add_fd(retval, &fdi); } } void sinsp_parser::parse_getrlimit_setrlimit_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; sinsp_evt *enter_evt = &m_tmp_evt; uint8_t resource; int64_t curval; if(evt->m_tinfo == nullptr) { return; } // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); // // Check if the syscall was successful // if(retval >= 0) { // // Load the enter event so we can access its arguments // if(!retrieve_enter_event(enter_evt, evt)) { return; } // // Extract the resource number // parinfo = enter_evt->get_param(0); resource = *(uint8_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(uint8_t)); if(resource == PPM_RLIMIT_NOFILE) { // // Extract the current value for the resource // parinfo = evt->get_param(1); curval = *(uint64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(uint64_t)); #ifdef _DEBUG if(evt->get_type() == PPME_SYSCALL_GETRLIMIT_X) { if(evt->m_tinfo->get_main_thread()->m_fdlimit != -1) { // ASSERT(curval == evt->m_tinfo->get_main_thread()->m_fdlimit); } } #endif if(curval != -1) { evt->m_tinfo->get_main_thread()->m_fdlimit = curval; } else { ASSERT(false); } } } } void sinsp_parser::parse_prlimit_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; sinsp_evt *enter_evt = &m_tmp_evt; uint8_t resource; int64_t newcur; int64_t tid; // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); // // Check if the syscall was successful // if(retval >= 0) { // // Load the enter event so we can access its arguments // if(!retrieve_enter_event(enter_evt, evt)) { return; } // // Extract the resource number // parinfo = enter_evt->get_param(1); resource = *(uint8_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(uint8_t)); if(resource == PPM_RLIMIT_NOFILE) { // // Extract the current value for the resource // parinfo = evt->get_param(1); newcur = *(uint64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(uint64_t)); if(newcur != -1) { // // Extract the tid and look for its process info // parinfo = enter_evt->get_param(0); tid = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); if(tid == 0) { tid = evt->get_tid(); } sinsp_threadinfo* ptinfo = m_inspector->get_thread(tid, true, true); if(ptinfo == NULL) { ASSERT(false); return; } // // update the process fdlimit // ptinfo->get_main_thread()->m_fdlimit = newcur; } } } } void sinsp_parser::parse_select_poll_epollwait_enter(sinsp_evt *evt) { if(evt->m_tinfo == NULL) { ASSERT(false); return; } if(evt->m_tinfo->m_lastevent_data == NULL) { evt->m_tinfo->m_lastevent_data = reserve_event_buffer(); } *(uint64_t*)evt->m_tinfo->m_lastevent_data = evt->get_ts(); } void sinsp_parser::parse_fcntl_enter(sinsp_evt *evt) { if(!evt->m_tinfo) { return; } sinsp_evt_param *parinfo = evt->get_param(1); ASSERT(parinfo->m_len == sizeof(int8_t)); uint8_t cmd = *(int8_t *)parinfo->m_val; if(cmd == PPM_FCNTL_F_DUPFD || cmd == PPM_FCNTL_F_DUPFD_CLOEXEC) { store_event(evt); } } void sinsp_parser::parse_fcntl_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; sinsp_evt *enter_evt = &m_tmp_evt; // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); // // If this is not a F_DUPFD or F_DUPFD_CLOEXEC command, ignore it // if(!retrieve_enter_event(enter_evt, evt)) { return; } // // Check if the syscall was successful // if(retval >= 0) { if(evt->m_fdinfo == NULL) { return; } // // Add the new fd to the table. // NOTE: dup2 and dup3 accept an existing FD and in that case they close it. // For us it's ok to just overwrite it. // evt->m_fdinfo = evt->m_tinfo->add_fd(retval, evt->m_fdinfo); } } void sinsp_parser::parse_context_switch(sinsp_evt* evt) { if(evt->m_tinfo) { sinsp_evt_param *parinfo; parinfo = evt->get_param(1); evt->m_tinfo->m_pfmajor = *(uint64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(uint64_t)); parinfo = evt->get_param(2); evt->m_tinfo->m_pfminor = *(uint64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(uint64_t)); auto main_tinfo = evt->m_tinfo->get_main_thread(); if(main_tinfo) { parinfo = evt->get_param(3); main_tinfo->m_vmsize_kb = *(uint32_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(uint32_t)); parinfo = evt->get_param(4); main_tinfo->m_vmrss_kb = *(uint32_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(uint32_t)); parinfo = evt->get_param(5); main_tinfo->m_vmswap_kb = *(uint32_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(uint32_t)); } } } void sinsp_parser::parse_brk_munmap_mmap_exit(sinsp_evt* evt) { ASSERT(evt->m_tinfo); if(evt->m_tinfo) { sinsp_evt_param *parinfo; parinfo = evt->get_param(1); evt->m_tinfo->m_vmsize_kb = *(uint32_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(uint32_t)); parinfo = evt->get_param(2); evt->m_tinfo->m_vmrss_kb = *(uint32_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(uint32_t)); parinfo = evt->get_param(3); evt->m_tinfo->m_vmswap_kb = *(uint32_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(uint32_t)); } } void sinsp_parser::parse_setresuid_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; sinsp_evt *enter_evt = &m_tmp_evt; // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); if(retval >= 0 && retrieve_enter_event(enter_evt, evt)) { parinfo = enter_evt->get_param(1); ASSERT(parinfo->m_len == sizeof(uint32_t)); uint32_t new_euid = *(uint32_t *)parinfo->m_val; if(new_euid < std::numeric_limits::max()) { if (evt->get_thread_info()) { evt->get_thread_info()->m_uid = new_euid; } } } } void sinsp_parser::parse_setresgid_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; sinsp_evt *enter_evt = &m_tmp_evt; // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); if(retval >= 0 && retrieve_enter_event(enter_evt, evt)) { parinfo = enter_evt->get_param(1); ASSERT(parinfo->m_len == sizeof(uint32_t)); uint32_t new_egid = *(uint32_t *)parinfo->m_val; if(new_egid < std::numeric_limits::max()) { if (evt->get_thread_info()) { evt->get_thread_info()->m_gid = new_egid; } } } } void sinsp_parser::parse_setuid_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; sinsp_evt *enter_evt = &m_tmp_evt; // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); if(retval >= 0 && retrieve_enter_event(enter_evt, evt)) { parinfo = enter_evt->get_param(0); ASSERT(parinfo->m_len == sizeof(uint32_t)); uint32_t new_euid = *(uint32_t *)parinfo->m_val; if (evt->get_thread_info()) { evt->get_thread_info()->m_uid = new_euid; } } } void sinsp_parser::parse_setgid_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; sinsp_evt *enter_evt = &m_tmp_evt; // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); if(retval >= 0 && retrieve_enter_event(enter_evt, evt)) { parinfo = enter_evt->get_param(0); ASSERT(parinfo->m_len == sizeof(uint32_t)); uint32_t new_egid = *(uint32_t *)parinfo->m_val; if (evt->get_thread_info()) { evt->get_thread_info()->m_gid = new_egid; } } } namespace { std::string generate_error_message(const Json::Value& value, const char* field) { std::string val_as_string = value.isConvertibleTo(Json::stringValue) ? value.asString().c_str() : "value not convertible to string"; std::string err_msg = "Unable to convert json value '" + val_as_string + "' for the field: '" + field +"'"; return std::move(err_msg); } bool check_int64_json_is_convertible(const Json::Value& value, const char* field) { if(!value.isNull()) { // isConvertibleTo doesn't seem to work on large 64 bit numbers if(value.isInt64()) { return true; } else { std::string err_msg = generate_error_message(value, field); SINSP_DEBUG("%s",err_msg.c_str()); } } return false; } bool check_json_val_is_convertible(const Json::Value& value, Json::ValueType other, const char* field, bool log_message=false) { if(value.isNull()) { return false; } if(!value.isConvertibleTo(other)) { std::string err_msg; if(log_message) { err_msg = generate_error_message(value, field); SINSP_WARNING("%s",err_msg.c_str()); } else { if(g_logger.get_severity() >= sinsp_logger::SEV_DEBUG) { err_msg = generate_error_message(value, field); SINSP_DEBUG("%s",err_msg.c_str()); } } return false; } return true; } } void sinsp_parser::parse_container_json_evt(sinsp_evt *evt) { sinsp_evt_param *parinfo = evt->get_param(0); ASSERT(parinfo); ASSERT(parinfo->m_len > 0); std::string json(parinfo->m_val, parinfo->m_len); SINSP_DEBUG("Parsing Container JSON=%s", json.c_str()); ASSERT(m_inspector); Json::Value root; if(Json::Reader().parse(json, root)) { sinsp_container_info container_info; const Json::Value& container = root["container"]; const Json::Value& id = container["id"]; if(check_json_val_is_convertible(id, Json::stringValue, "id")) { container_info.m_id = id.asString(); } const Json::Value& type = container["type"]; if(check_json_val_is_convertible(type, Json::uintValue, "type")) { container_info.m_type = static_cast(type.asUInt()); } const Json::Value& name = container["name"]; if(check_json_val_is_convertible(name, Json::stringValue, "name")) { container_info.m_name = name.asString(); } const Json::Value& is_pod_sandbox = container["is_pod_sandbox"]; if(check_json_val_is_convertible(is_pod_sandbox, Json::booleanValue, "is_pod_sandbox")) { container_info.m_is_pod_sandbox = is_pod_sandbox.asBool(); } const Json::Value& image = container["image"]; if(check_json_val_is_convertible(image, Json::stringValue, "image")) { container_info.m_image = image.asString(); } const Json::Value& imageid = container["imageid"]; if(check_json_val_is_convertible(imageid, Json::stringValue, "imageid")) { container_info.m_imageid = imageid.asString(); } const Json::Value& imagerepo = container["imagerepo"]; if(check_json_val_is_convertible(imagerepo, Json::stringValue, "imagerepo")) { container_info.m_imagerepo = imagerepo.asString(); } const Json::Value& imagetag = container["imagetag"]; if(check_json_val_is_convertible(imagetag, Json::stringValue, "imagetag")) { container_info.m_imagetag = imagetag.asString(); } const Json::Value& imagedigest = container["imagedigest"]; if(check_json_val_is_convertible(imagedigest, Json::stringValue, "imagedigest")) { container_info.m_imagedigest = imagedigest.asString(); } const Json::Value& privileged = container["privileged"]; if(check_json_val_is_convertible(privileged, Json::booleanValue, "privileged")) { container_info.m_privileged = privileged.asBool(); } libsinsp::container_engine::docker::parse_json_mounts(container["Mounts"], container_info.m_mounts); sinsp_container_info::container_health_probe::parse_health_probes(container, container_info.m_health_probes); const Json::Value& contip = container["ip"]; if(check_json_val_is_convertible(contip, Json::stringValue, "ip")) { uint32_t ip; if(inet_pton(AF_INET, contip.asString().c_str(), &ip) == -1) { throw sinsp_exception("Invalid 'ip' field while parsing container info: " + json); } container_info.m_container_ip = ntohl(ip); } const Json::Value &port_mappings = container["port_mappings"]; if(check_json_val_is_convertible(port_mappings, Json::arrayValue, "port_mappings")) { for (Json::Value::ArrayIndex i = 0; i != port_mappings.size(); i++) { sinsp_container_info::container_port_mapping map; const Json::Value &host_ip = port_mappings[i]["HostIp"]; // We log message for HostIp conversion failure at Warning level if(check_json_val_is_convertible(host_ip, Json::intValue, "HostIp", true)) { map.m_host_ip = host_ip.asInt(); } const Json::Value& host_port = port_mappings[i]["HostPort"]; // We log message for HostPort conversion failure at Warning level if(check_json_val_is_convertible(host_port, Json::intValue, "HostPort", true)) { map.m_host_port = (uint16_t) host_port.asInt(); } const Json::Value& container_port = port_mappings[i]["ContainerPort"]; // We log message for ContainerPort conversion failure at Warning level if(check_json_val_is_convertible(container_port, Json::intValue, "ContainerPort", true)) { map.m_container_port = (uint16_t) container_port.asInt(); } container_info.m_port_mappings.push_back(map); } } vector labels = container["labels"].getMemberNames(); for(vector::const_iterator it = labels.begin(); it != labels.end(); ++it) { string val = container["labels"][*it].asString(); container_info.m_labels[*it] = val; } const Json::Value& env_vars = container["env"]; for(const auto& env_var : env_vars) { if(env_var.isString()) { container_info.m_env.emplace_back(env_var.asString()); } } const Json::Value& memory_limit = container["memory_limit"]; if(check_int64_json_is_convertible(memory_limit, "memory_limit")) { container_info.m_memory_limit = memory_limit.asInt64(); } const Json::Value& swap_limit = container["swap_limit"]; if(check_int64_json_is_convertible(swap_limit, "swap_limit")) { container_info.m_swap_limit = swap_limit.asInt64(); } const Json::Value& cpu_shares = container["cpu_shares"]; if(check_int64_json_is_convertible(cpu_shares, "cpu_shares")) { container_info.m_cpu_shares = cpu_shares.asInt64(); } const Json::Value& cpu_quota = container["cpu_quota"]; if(check_int64_json_is_convertible(cpu_quota, "cpu_quota")) { container_info.m_cpu_quota = cpu_quota.asInt64(); } const Json::Value& cpu_period = container["cpu_period"]; if(check_int64_json_is_convertible(cpu_period, "cpu_period")) { container_info.m_cpu_period = cpu_period.asInt64(); } const Json::Value& cpuset_cpu_count = container["cpuset_cpu_count"]; if(check_json_val_is_convertible(cpuset_cpu_count, Json::intValue, "cpuset_cpu_count")) { container_info.m_cpuset_cpu_count = cpuset_cpu_count.asInt(); } const Json::Value& mesos_task_id = container["mesos_task_id"]; if(check_json_val_is_convertible(mesos_task_id, Json::stringValue, "mesos_task_id")) { container_info.m_mesos_task_id = mesos_task_id.asString(); } const Json::Value& metadata_deadline = container["metadata_deadline"]; if(!metadata_deadline.isNull()) { // isConvertibleTo doesn't seem to work on large 64 bit numbers if(metadata_deadline.isUInt64()) { container_info.m_metadata_deadline = metadata_deadline.asUInt64(); } else { SINSP_DEBUG("Unable to convert json value for field: %s", "metadata_deadline"); } } evt->m_tinfo_ref = container_info.get_tinfo(m_inspector); evt->m_tinfo = evt->m_tinfo_ref.get(); m_inspector->m_container_manager.add_container(container_info, evt->get_thread_info(true)); /* SINSP_STR_DEBUG("Container\n-------\nID:" + container_info.m_id + "\nType: " + std::to_string(container_info.m_type) + "\nName: " + container_info.m_name + "\nImage: " + container_info.m_image + "\nMesos Task ID: " + container_info.m_mesos_task_id); */ } else { std::string errstr; errstr = Json::Reader().getFormattedErrorMessages(); throw sinsp_exception("Invalid JSON encountered while parsing container info: " + json + "error=" + errstr); } } void sinsp_parser::parse_container_evt(sinsp_evt *evt) { sinsp_evt_param *parinfo; sinsp_container_info container_info; parinfo = evt->get_param(0); container_info.m_id = parinfo->m_val; parinfo = evt->get_param(1); ASSERT(parinfo->m_len == sizeof(uint32_t)); container_info.m_type = (sinsp_container_type) *(uint32_t *)parinfo->m_val; parinfo = evt->get_param(2); container_info.m_name = parinfo->m_val; parinfo = evt->get_param(3); container_info.m_image = parinfo->m_val; m_inspector->m_container_manager.add_container(container_info, evt->get_thread_info(true)); } void sinsp_parser::parse_cpu_hotplug_enter(sinsp_evt *evt) { if(m_inspector->is_live()) { throw sinsp_exception("CPU " + evt->get_param_value_str("cpu") + " configuration change detected. Aborting."); } } uint8_t* sinsp_parser::reserve_event_buffer() { if(m_tmp_events_buffer.empty()) { return (uint8_t*)malloc(sizeof(uint8_t)*SP_EVT_BUF_SIZE); } else { auto ptr = m_tmp_events_buffer.top(); m_tmp_events_buffer.pop(); return ptr; } } #ifndef CYGWING_AGENT int sinsp_parser::get_k8s_version(const std::string& json) { if(m_k8s_capture_version == k8s_state_t::CAPTURE_VERSION_NONE) { SINSP_STR_DEBUG(json); Json::Value root; if(Json::Reader().parse(json, root)) { const Json::Value& items = root["items"]; // new if(!items.isNull()) { SINSP_STR_DEBUG("K8s capture version " + std::to_string(k8s_state_t::CAPTURE_VERSION_2) + " detected."); m_k8s_capture_version = k8s_state_t::CAPTURE_VERSION_2; return m_k8s_capture_version; } const Json::Value& object = root["object"]; // old if(!object.isNull()) { SINSP_STR_DEBUG("K8s capture version " + std::to_string(k8s_state_t::CAPTURE_VERSION_2) + " detected."); m_k8s_capture_version = k8s_state_t::CAPTURE_VERSION_1; return m_k8s_capture_version; } throw sinsp_exception("Unrecognized K8s capture format."); } else { std::string errstr; errstr = Json::Reader().getFormattedErrorMessages(); throw sinsp_exception("Invalid K8s capture JSON encountered (" + errstr + ")"); } } return m_k8s_capture_version; } void sinsp_parser::parse_k8s_evt(sinsp_evt *evt) { sinsp_evt_param *parinfo = evt->get_param(0); ASSERT(parinfo); ASSERT(parinfo->m_len > 0); std::string json(parinfo->m_val, parinfo->m_len); //SINSP_STR_DEBUG(json); ASSERT(m_inspector); if(!m_inspector) { throw sinsp_exception("Inspector is null, K8s client can not be created."); } if(!m_inspector->m_k8s_client) { m_inspector->make_k8s_client(); } if(m_inspector->m_k8s_client) { m_inspector->m_k8s_client->simulate_watch_event(std::move(json), get_k8s_version(json)); } else { throw sinsp_exception("K8s client can not be created."); } } void sinsp_parser::parse_mesos_evt(sinsp_evt *evt) { sinsp_evt_param *parinfo = evt->get_param(0); ASSERT(parinfo); ASSERT(parinfo->m_len > 0); std::string json(parinfo->m_val, parinfo->m_len); //SINSP_STR_DEBUG(json); ASSERT(m_inspector); ASSERT(m_inspector->m_mesos_client); m_inspector->m_mesos_client->simulate_event(json); } #endif // CYGWING_AGENT void sinsp_parser::parse_chroot_exit(sinsp_evt *evt) { auto parinfo = evt->get_param(0); auto retval = *(int64_t *)parinfo->m_val; if(retval == 0 && evt->m_tinfo != nullptr) { const char* resolved_path; auto path = evt->get_param_as_str(1, &resolved_path); if(resolved_path[0] == 0) { evt->m_tinfo->m_root = path; } else { evt->m_tinfo->m_root = resolved_path; } // Root change, let's detect if we are on a container ASSERT(m_inspector); m_inspector->m_container_manager.resolve_container(evt->m_tinfo, m_inspector->is_live()); } } void sinsp_parser::parse_setsid_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); if(retval >= 0) { if (evt->get_thread_info()) { evt->get_thread_info()->m_sid = retval; } } } void sinsp_parser::parse_getsockopt_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; int64_t err; int64_t fd; int8_t level, optname; if(!evt->m_tinfo) { return; } parinfo = evt->get_param(1); fd = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); evt->m_fdinfo = evt->m_tinfo->get_main_thread()->get_fd(fd); evt->m_tinfo->m_lastevent_fd = fd; // right now we only parse getsockopt() for SO_ERROR options // if that ever changes, move this check inside // the `if (level == PPM_SOCKOPT_LEVEL_SOL_SOCKET ...)` block if (!m_track_connection_status) { return; } //evt->m_fdinfo = evt->m_tinfo->get_fd(evt->m_tinfo->m_lastevent_fd); // // Extract the return value // parinfo = evt->get_param(0); retval = *(int64_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int64_t)); if(retval < 0) { return; } parinfo = evt->get_param(2); level = *(int8_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int8_t)); parinfo = evt->get_param(3); optname = *(int8_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(int8_t)); if(level == PPM_SOCKOPT_LEVEL_SOL_SOCKET && optname == PPM_SOCKOPT_SO_ERROR) { if (!evt->m_fdinfo) { return; } parinfo = evt->get_param(4); ASSERT(*parinfo->m_val == PPM_SOCKOPT_IDX_ERRNO); ASSERT(parinfo->m_len == sizeof(int64_t) + 1); err = *(int64_t *)(parinfo->m_val + 1); // add 1 byte to skip over PT_DYN param index evt->m_errorcode = (int32_t)err; if (err < 0) { evt->m_fdinfo->set_socket_failed(); } else { evt->m_fdinfo->set_socket_connected(); } if (m_fd_listener) { m_fd_listener->on_socket_status_changed(evt); } } } void sinsp_parser::free_event_buffer(uint8_t *ptr) { if(m_tmp_events_buffer.size() < m_inspector->m_thread_manager->m_threadtable.size()) { m_tmp_events_buffer.push(ptr); } else { free(ptr); } } sysdig-0.26.4/userspace/libsinsp/parsers.h000066400000000000000000000141301352731327100205550ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ //////////////////////////////////////////////////////////////////////////// // Public definitions for the scap library //////////////////////////////////////////////////////////////////////////// #pragma once #include "sinsp.h" class sinsp_fd_listener; class metaevents_state { public: bool m_new_group; uint32_t m_n_additional_events_to_add; sinsp_evt m_metaevt; scap_evt* m_piscapevt; uint32_t m_scap_buf_size; }; class sinsp_parser { public: sinsp_parser(sinsp* inspector); ~sinsp_parser(); // // Processing entry point // void process_event(sinsp_evt* evt); void event_cleanup(sinsp_evt* evt); void erase_fd(erase_fd_params* params); // // Get the enter event matching the last received event // bool retrieve_enter_event(sinsp_evt* enter_evt, sinsp_evt* exit_evt); // // Combine the openat arguments into a full file name // static void parse_openat_dir(sinsp_evt *evt, char* name, int64_t dirfd, OUT string* sdir); // // Protocol decoder infrastructure methods // sinsp_protodecoder* add_protodecoder(string decoder_name); void register_event_callback(sinsp_pd_callback_type etype, sinsp_protodecoder* dec); void schedule_k8s_events(); void schedule_mesos_events(); // // Protocol decoders callback lists // vector m_open_callbacks; vector m_connect_callbacks; ppm_event_flags m_drop_event_flags; // // Initializers // static void init_scapevt(metaevents_state& evt_state, uint16_t evt_type, uint16_t buf_size); private: // // Initializers // inline void init_metaevt(metaevents_state& evt_state, uint16_t evt_type, uint16_t buf_size); // // Helpers // bool reset(sinsp_evt *evt); inline void store_event(sinsp_evt* evt); // // Parsers // void parse_clone_exit(sinsp_evt* evt); void parse_execve_exit(sinsp_evt* evt); void proc_schedule_removal(sinsp_evt* evt); void parse_open_openat_creat_exit(sinsp_evt* evt); void parse_pipe_exit(sinsp_evt* evt); void parse_socketpair_exit(sinsp_evt* evt); void parse_socket_exit(sinsp_evt* evt); void parse_connect_exit(sinsp_evt* evt); void parse_accept_exit(sinsp_evt* evt); void parse_close_enter(sinsp_evt* evt); void parse_close_exit(sinsp_evt* evt); void parse_thread_exit(sinsp_evt* evt); inline bool detect_and_process_tracer_write(sinsp_evt *evt, int64_t retval, ppm_event_flags eflags); inline void parse_rw_exit(sinsp_evt* evt); void parse_sendfile_exit(sinsp_evt* evt); void parse_eventfd_exit(sinsp_evt* evt); void parse_bind_exit(sinsp_evt* evt); void parse_chdir_exit(sinsp_evt* evt); void parse_fchdir_exit(sinsp_evt* evt); void parse_getcwd_exit(sinsp_evt* evt); void parse_shutdown_exit(sinsp_evt* evt); void parse_dup_exit(sinsp_evt* evt); void parse_signalfd_exit(sinsp_evt* evt); void parse_timerfd_create_exit(sinsp_evt* evt); void parse_inotify_init_exit(sinsp_evt* evt); void parse_getrlimit_setrlimit_exit(sinsp_evt* evt); void parse_prlimit_exit(sinsp_evt* evt); void parse_select_poll_epollwait_enter(sinsp_evt *evt); void parse_fcntl_enter(sinsp_evt* evt); void parse_fcntl_exit(sinsp_evt* evt); void parse_context_switch(sinsp_evt* evt); void parse_brk_munmap_mmap_exit(sinsp_evt* evt); void parse_setresuid_exit(sinsp_evt* evt); void parse_setresgid_exit(sinsp_evt* evt); void parse_setuid_exit(sinsp_evt* evt); void parse_setgid_exit(sinsp_evt* evt); void parse_container_evt(sinsp_evt* evt); // deprecated, only for backward-compatibility void parse_container_json_evt(sinsp_evt *evt); inline uint32_t parse_tracer(sinsp_evt *evt, int64_t retval); void parse_cpu_hotplug_enter(sinsp_evt* evt); int get_k8s_version(const std::string& json); #ifndef CYGWING_AGENT void parse_k8s_evt(sinsp_evt *evt); void parse_mesos_evt(sinsp_evt *evt); #endif void parse_chroot_exit(sinsp_evt *evt); void parse_setsid_exit(sinsp_evt *evt); void parse_getsockopt_exit(sinsp_evt *evt); inline void add_socket(sinsp_evt* evt, int64_t fd, uint32_t domain, uint32_t type, uint32_t protocol); inline void infer_sendto_fdinfo(sinsp_evt *evt); inline void add_pipe(sinsp_evt *evt, int64_t tid, int64_t fd, uint64_t ino); // Return false if the update didn't happen (for example because the tuple is NULL) bool update_fd(sinsp_evt *evt, sinsp_evt_param* parinfo); // Next 4 return false if the update didn't happen because the tuple is identical to the given address bool set_ipv4_addresses_and_ports(sinsp_fdinfo_t* fdinfo, uint8_t* packed_data); bool set_ipv4_mapped_ipv6_addresses_and_ports(sinsp_fdinfo_t* fdinfo, uint8_t* packed_data); bool set_ipv6_addresses_and_ports(sinsp_fdinfo_t* fdinfo, uint8_t* packed_data); bool set_unix_info(sinsp_fdinfo_t* fdinfo, uint8_t* packed_data); void swap_addresses(sinsp_fdinfo_t* fdinfo); uint8_t* reserve_event_buffer(); void free_event_buffer(uint8_t*); // // Pointers to inspector context // sinsp* m_inspector; // // Temporary storage to avoid memory allocation // sinsp_evt m_tmp_evt; uint8_t m_fake_userevt_storage[4096]; scap_evt* m_fake_userevt; string m_tracer_error_string; bool m_track_connection_status = false; // FD listener callback sinsp_fd_listener* m_fd_listener; // // The protocol decoders allocated by this parser // vector m_protodecoders; metaevents_state m_k8s_metaevents_state; int m_k8s_capture_version = -1; metaevents_state m_mesos_metaevents_state; stack m_tmp_events_buffer; friend class sinsp_analyzer; friend class sinsp_analyzer_fd_listener; friend class sinsp_protodecoder; friend class sinsp_baseliner; friend class sinsp_container_manager; }; sysdig-0.26.4/userspace/libsinsp/prefix_search.cpp000066400000000000000000000036601352731327100222610ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "prefix_search.h" using namespace std; path_prefix_search::path_prefix_search() { } path_prefix_search::~path_prefix_search() { } void path_prefix_search::add_search_path(const char *path) { bool dummy = true; return path_prefix_map::add_search_path(path, dummy); } void path_prefix_search::add_search_path(const filter_value_t &path) { bool dummy = true; return path_prefix_map::add_search_path(path, dummy); } bool path_prefix_search::match(const char *path) { const bool *val = path_prefix_map::match(path); return (val != NULL); } bool path_prefix_search::match(const filter_value_t &path) { const bool *val = path_prefix_map::match(path); return (val != NULL); } std::string path_prefix_search::as_string() { return path_prefix_map::as_string(false); } void path_prefix_map_ut::split_path(const filter_value_t &path, filter_components_t &components) { components.clear(); uint8_t *pos = path.first; while (pos < path.first + path.second) { uint8_t *sep = (uint8_t *) memchr((char *) pos, '/', path.second - (pos - path.first)); if (sep) { if (sep-pos > 0) { components.emplace_back(pos, sep-pos); } pos = sep + 1; } else { components.emplace_back(pos, path.second - (pos - path.first)); pos = path.first + path.second + 1; } } } sysdig-0.26.4/userspace/libsinsp/prefix_search.h000066400000000000000000000227671352731327100217370ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #include #include #include #include #include "filter_value.h" namespace path_prefix_map_ut { typedef std::list filter_components_t; // Split path /var/log/messages into a list of components (var, log, messages). Empty components are skipped. void split_path(const filter_value_t &path, filter_components_t &components); }; // // A data structure that allows testing a path P against a set of // search paths S. The search succeeds if any of the search paths Si // is a prefix of the path P. // // Here are some examples: // - search(/var/run/docker, [/var/run, /etc, /lib, /usr/lib]) // succeeds because /var/run is a prefix of /var/run/docker. // - search(/boot, [/var/run, /etc, /lib, /usr/lib]) // does not succeed because no path is a prefix of /boot. // - search(/var/lib/messages, [/var/run, /etc, /lib, /usr/lib]) // does not succeed because no path is a prefix of /var/lib/messages. // /var is a partial match but not /var/run. // - search(/var, [/var/run, /etc, /lib, /usr/lib]) // does not succeed because no path is a prefix of /var // /var is a partial match but the search path is /var/run, not /var. template class path_prefix_map { public: path_prefix_map(); virtual ~path_prefix_map(); void add_search_path(const char *path, Value &v); void add_search_path(const filter_value_t &path, Value &v); // Similar to add_search_path, but takes a path already split // into a list of components. This allows for custom splitting // of paths other than on '/' boundaries. void add_search_path_components(const path_prefix_map_ut::filter_components_t &components, Value &v); // If non-NULL, Value is not allocated. It points to memory // held within this path_prefix_map() and is only valid as // long as the map exists. Value * match(const char *path); Value * match(const filter_value_t &path); Value *match_components(const path_prefix_map_ut::filter_components_t &components); std::string as_string(bool include_vals); private: std::string as_string(const std::string &prefix, bool include_vals); void add_search_path_components(const path_prefix_map_ut::filter_components_t &components, path_prefix_map_ut::filter_components_t::const_iterator comp, Value &v); Value *match_components(const path_prefix_map_ut::filter_components_t &components, path_prefix_map_ut::filter_components_t::const_iterator comp); // Maps from the path component at the current level to a // prefix search for the sub-path below the current level. // For example, if the set of search paths is (/var/run, /etc, // /lib, /usr, /usr/lib, /var/lib, /var/run), m_dirs contains: // - (var, path_prefix_map(/run) // - (etc, NULL) // - (lib, NULL) // - (usr, NULL) // - (var, path_prefix_map(/lib, /run) // Note that because usr is a prefix of /usr/lib, the /usr/lib // path is dropped and only /usr is kept. Also note that // terminator paths have a NULL path_prefix_map object. std::unordered_map, g_hash_membuf, g_equal_to_membuf> m_dirs; }; template path_prefix_map::path_prefix_map() { } template path_prefix_map::~path_prefix_map() { for (auto &ent : m_dirs) { delete(ent.second.first); delete(ent.second.second); } } // NOTE: this does not copy, so it is only valid as long as path is valid. template void path_prefix_map::add_search_path(const char *path, Value &v) { filter_value_t mem((uint8_t *) path, (uint32_t) strlen(path)); return add_search_path(mem, v); } template void path_prefix_map::add_search_path(const filter_value_t &path, Value &v) { path_prefix_map_ut::filter_components_t components; path_prefix_map_ut::split_path(path, components); // Add an initial "root" to the set of components. That // ensures that a top-level path of '/' still results in a // non-empty components list. For all other paths, there will // be a dummy 'root' prefix at the top of every path. components.emplace_front((uint8_t *) "root", 4); return add_search_path_components(components, v); } template void path_prefix_map::add_search_path_components(const path_prefix_map_ut::filter_components_t &components, Value &v) { add_search_path_components(components, components.begin(), v); } template void path_prefix_map::add_search_path_components(const path_prefix_map_ut::filter_components_t &components, path_prefix_map_ut::filter_components_t::const_iterator comp, Value &v) { path_prefix_map *subtree = NULL; auto it = m_dirs.find(*comp); auto cur = comp; comp++; if(it == m_dirs.end()) { // This path component doesn't match any existing // dirent. We need to add one and its subtree. if(comp != components.end()) { subtree = new path_prefix_map(); subtree->add_search_path_components(components, comp, v); } // If the path doesn't have anything remaining, we // also add the value here. m_dirs[*cur] = std::pair(subtree, (comp == components.end() ? new Value(v) : NULL)); } else { // An entry for this dirent already exists. We will // either add a new entry to the subtree, do nothing, // or get rid of the existing subtree. if(comp == components.end()) { // This path is a prefix of the current path and we // can drop the existing subtree. For example, we can // drop /usr/lib when adding /usr. delete(it->second.first); delete(it->second.second); m_dirs.erase(*cur); m_dirs[*cur] = std::pair(NULL, new Value(v)); } else if(it->second.first == NULL) { // The existing path is shorter than the // current path, in which case we don't have // to do anything. For example, no need to add // /usr/lib when /usr exists. } else { // We need to add the remainder to the // sub-tree's search path. it->second.first->add_search_path_components(components, comp, v); } } } // NOTE: this does not copy, so it is only valid as long as path is valid. template Value *path_prefix_map::match(const char *path) { filter_value_t mem((uint8_t *) path, (uint32_t) strlen(path)); return match(mem); } template Value *path_prefix_map::match(const filter_value_t &path) { path_prefix_map_ut::filter_components_t components; path_prefix_map_ut::split_path(path, components); // Add an initial "root" to the set of components. That // ensures that a top-level path of '/' still results in a // non-empty components list. For all other paths, there will // be a dummy 'root' prefix at the top of every path. components.emplace_front((uint8_t *) "root", 4); return match_components(components); } template Value *path_prefix_map::match_components(const path_prefix_map_ut::filter_components_t &components) { return match_components(components, components.begin()); } template Value *path_prefix_map::match_components(const path_prefix_map_ut::filter_components_t &components, path_prefix_map_ut::filter_components_t::const_iterator comp) { auto it = m_dirs.find(*comp); comp++; if(it == m_dirs.end()) { return NULL; } else { // If there is nothing left in the match path, the // subtree must be null. This ensures that /var // matches only /var and not /var/lib if(comp == components.end()) { if(it->second.first == NULL) { return it->second.second; } else { return NULL; } } else if(it->second.first == NULL) { // /foo/bar matched a prefix /foo, so we're // done. return it->second.second; } else { return it->second.first->match_components(components, comp); } } } template std::string path_prefix_map::as_string(bool include_vals) { return as_string(std::string(""), include_vals); } // Unlike all the other methods, this does perform copies. template std::string path_prefix_map::as_string(const std::string &prefix, bool include_vals) { std::ostringstream os; for (auto &it : m_dirs) { std::string dirent((const char *) it.first.first, it.first.second); os << prefix << dirent << " -> "; if (include_vals && it.second.first == NULL) { os << "v=" << (*it.second.second); } os << std::endl; if(it.second.first) { std::string indent = prefix; indent += " "; os << it.second.first->as_string(indent, include_vals); } } return os.str(); } class path_prefix_search : public path_prefix_map { public: path_prefix_search(); ~path_prefix_search(); void add_search_path(const char *path); void add_search_path(const filter_value_t &path); // If non-NULL, Value is not allocated. It points to memory // held within this path_prefix_map() and is only valid as // long as the map exists. bool match(const char *path); bool match(const filter_value_t &path); std::string as_string(); }; sysdig-0.26.4/userspace/libsinsp/procinfo_test.cpp000066400000000000000000000100571352731327100223130ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "sinsp.h" #include "sinsp_int.h" #include "../../driver/ppm_events_public.h" #include sinsp_procinfo make_procinfo(sinsp* inspector, int64_t tid, int64_t tgid) { sinsp_procinfo procinfo(inspector); procinfo.m_tid = tid; procinfo.m_tgid = tgid; return procinfo; } sinsp_procinfo make_procinfo(int64_t tid, int64_t tgid) { sinsp_procinfo procinfo; procinfo.m_tid = tid; procinfo.m_tgid = tgid; return procinfo; } sinsp_procinfo make_procinfo(int64_t tid) { return make_procinfo(tid,tid); } TEST(procinfo_single_thread,add_non_existing_fd) { sinsp_procinfo procinfo = make_procinfo(0); sinsp_fdinfo fdinfo; procinfo.add_fd(0, &fdinfo); EXPECT_EQ(1, procinfo.m_fdtable.count(0)); } TEST(procinfo_single_thread,add_existing_fd) { sinsp_procinfo procinfo = make_procinfo(0); sinsp_fdinfo fdinfo1; fdinfo1.m_name = "a"; sinsp_fdinfo fdinfo2; fdinfo2.m_name = "b"; procinfo.add_fd(0, &fdinfo1); procinfo.add_fd(0, &fdinfo2); EXPECT_EQ("b", procinfo.m_fdtable[0].m_name); } TEST(procinfo_single_thread,get_existing_fd) { sinsp_procinfo procinfo = make_procinfo(0); sinsp_fdinfo fdinfo; fdinfo.m_name = "a"; procinfo.add_fd(0, &fdinfo); EXPECT_EQ("a", procinfo.get_fd(0)->m_name); } TEST(procinfo_single_thread,get_non_existing_fd) { sinsp_procinfo procinfo = make_procinfo(0); EXPECT_TRUE(NULL == procinfo.get_fd(0)); } TEST(procinfo_single_thread,remove_existing_fd) { sinsp_procinfo procinfo = make_procinfo(0); sinsp_fdinfo fdinfo; procinfo.add_fd(0, &fdinfo); procinfo.remove_fd(0); EXPECT_TRUE(NULL == procinfo.get_fd(0)); } TEST(procinfo_single_thread, remove_not_existing_fd) { sinsp_procinfo procinfo = make_procinfo(0); #ifdef _DEBUG ASSERT_DEATH(procinfo.remove_fd(0), ".*"); #else procinfo.remove_fd(0); #endif } TEST(procinfo_multi_thread,add_non_existing_fd) { sinsp inspector; sinsp_procinfo parent = make_procinfo(&inspector, 0, 0); sinsp_procinfo child = make_procinfo(&inspector, 1, 0); child.m_flags = PPM_CL_CLONE_FILES; inspector.add_process(parent); inspector.add_process(child); EXPECT_TRUE(NULL == inspector.get_process(1)->get_fd(0)); sinsp_fdinfo fdinfo; inspector.get_process(0)->add_fd(0, &fdinfo); EXPECT_TRUE(NULL != inspector.get_process(1)->get_fd(0)); } TEST(procinfo,get_fd_table_single_thread) { sinsp inspector; sinsp_procinfo parent = make_procinfo(&inspector, 0, 0); EXPECT_EQ(&(parent.m_fdtable), parent.get_fd_table()); } TEST(procinfo,get_fd_table_multi_thread) { sinsp inspector; // setup a process with a child thread sinsp_procinfo parent = make_procinfo(&inspector, 0, 0); sinsp_procinfo child = make_procinfo(&inspector, 1, 0); child.m_flags = PPM_CL_CLONE_FILES; inspector.add_process(parent); inspector.add_process(child); sinsp_procinfo* parent_proc = inspector.get_process(0); sinsp_procinfo* child_proc = inspector.get_process(1); // the child's fd table is the same as the parent's EXPECT_EQ(child_proc->get_fd_table(), parent_proc->get_fd_table()); } TEST(procinfo,get_root_process_single_thread) { sinsp inspector; sinsp_procinfo proc = make_procinfo(&inspector, 0, 0); EXPECT_EQ(&proc, proc.get_root_process()); } TEST(procinfo,get_root_process_child_clone) { sinsp inspector; sinsp_procinfo parent = make_procinfo(&inspector, 0, 0); sinsp_procinfo child = make_procinfo(&inspector, 1, 0); child.m_flags = PPM_CL_CLONE_FILES; inspector.add_process(parent); inspector.add_process(child); EXPECT_EQ(inspector.get_process(0), inspector.get_process(1)->get_root_process()); } sysdig-0.26.4/userspace/libsinsp/protodecoder.cpp000066400000000000000000000154331352731327100221310ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #ifndef _WIN32 #include #endif #include "sinsp.h" #include "sinsp_int.h" #include "protodecoder.h" extern sinsp_protodecoder_list g_decoderlist; /////////////////////////////////////////////////////////////////////////////// // sinsp_protodecoder implementation /////////////////////////////////////////////////////////////////////////////// sinsp_protodecoder::sinsp_protodecoder() { } void sinsp_protodecoder::set_inspector(sinsp* inspector) { m_inspector = inspector; } void sinsp_protodecoder::on_read(sinsp_evt* evt, char *data, uint32_t len) { ASSERT(false); } void sinsp_protodecoder::on_write(sinsp_evt* evt, char *data, uint32_t len) { ASSERT(false); } void sinsp_protodecoder::on_reset(sinsp_evt* evt) { ASSERT(false); } void sinsp_protodecoder::register_event_callback(sinsp_pd_callback_type etype) { ASSERT(m_inspector != NULL); m_inspector->m_parser->register_event_callback(etype, this); } void sinsp_protodecoder::register_read_callback(sinsp_fdinfo_t* fdinfo) { ASSERT(m_inspector != NULL); fdinfo->register_event_callback(CT_READ, this); } void sinsp_protodecoder::register_write_callback(sinsp_fdinfo_t* fdinfo) { ASSERT(m_inspector != NULL); fdinfo->register_event_callback(CT_WRITE, this); } void sinsp_protodecoder::unregister_read_callback(sinsp_fdinfo_t* fdinfo) { ASSERT(m_inspector != NULL); fdinfo->unregister_event_callback(CT_READ, this); } void sinsp_protodecoder::unregister_write_callback(sinsp_fdinfo_t* fdinfo) { ASSERT(m_inspector != NULL); fdinfo->unregister_event_callback(CT_WRITE, this); } /////////////////////////////////////////////////////////////////////////////// // sinsp_protodecoder_list implementation /////////////////////////////////////////////////////////////////////////////// sinsp_protodecoder_list::sinsp_protodecoder_list() { ////////////////////////////////////////////////////////////////////////////// // ADD NEW DECODER CLASSES HERE ////////////////////////////////////////////////////////////////////////////// add_protodecoder(new sinsp_decoder_syslog()); } sinsp_protodecoder_list::~sinsp_protodecoder_list() { uint32_t j; for(j = 0; j < m_decoders_list.size(); j++) { delete m_decoders_list[j]; } } void sinsp_protodecoder_list::add_protodecoder(sinsp_protodecoder* protodecoder) { m_decoders_list.push_back(protodecoder); } sinsp_protodecoder* sinsp_protodecoder_list::new_protodecoder_from_name(const string& name, sinsp* inspector) { uint32_t j; for(j = 0; j < m_decoders_list.size(); j++) { m_decoders_list[j]->m_inspector = inspector; if(m_decoders_list[j]->m_name == name) { sinsp_protodecoder* newchk = m_decoders_list[j]->allocate_new(); newchk->set_inspector(inspector); return newchk; } } throw sinsp_exception("unknown protocol decoder " + name); } /////////////////////////////////////////////////////////////////////////////// // sinsp_decoder_syslog implementation /////////////////////////////////////////////////////////////////////////////// const char* syslog_severity_strings[] = { "emerg", "alert", "crit", "err", "warn", "notice", "info", "debug" }; const char* syslog_facility_strings[] = { "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", "news", "uucp", "clock", "authpriv", "ftp", "ntp", "logaudit", "logalert", "cron", "local0", "local1", "local2", "local3", "local4", "local5", "local6", "local7" }; sinsp_decoder_syslog::sinsp_decoder_syslog() { m_name = "syslog"; m_priority = -1; } sinsp_protodecoder* sinsp_decoder_syslog::allocate_new() { return (sinsp_protodecoder*) new sinsp_decoder_syslog(); } void sinsp_decoder_syslog::init() { register_event_callback(CT_OPEN); register_event_callback(CT_CONNECT); } void sinsp_decoder_syslog::on_fd_from_proc(sinsp_fdinfo_t* fdinfo) { if(fdinfo == NULL) { ASSERT(false); return ; } if(fdinfo->m_name.find("/dev/log") != string::npos) { register_write_callback(fdinfo); } } void sinsp_decoder_syslog::on_event(sinsp_evt* evt, sinsp_pd_callback_type etype) { if(etype == CT_OPEN || etype == CT_CONNECT) { sinsp_fdinfo_t* fdinfo = evt->get_fd_info(); if(fdinfo == NULL) { return ; } if(fdinfo->m_name.find("/dev/log") != string::npos) { register_write_callback(fdinfo); } } else if(etype == CT_TUPLE_CHANGE) { sinsp_fdinfo_t* fdinfo = evt->get_fd_info(); if(fdinfo == NULL) { return ; } if(fdinfo->m_name.find("/dev/log") != string::npos) { register_write_callback(fdinfo); } else { if(fdinfo->has_decoder_callbacks()) { unregister_write_callback(fdinfo); } } } else { ASSERT(false); } } #define PRI_BUF_SIZE 16 void sinsp_decoder_syslog::on_write(sinsp_evt* evt, char *data, uint32_t len) { char pri[PRI_BUF_SIZE]; char* tc = data + 1; char* te = data + len; uint32_t j = 0; while(tc < te && *tc != '>' && *tc != '\0' && j < PRI_BUF_SIZE - 1) { pri[j++] = *tc; tc++; } pri[j] = 0; decode_message(data, len, pri, j); } void sinsp_decoder_syslog::on_reset(sinsp_evt* evt) { m_priority = -1; } bool sinsp_decoder_syslog::is_data_valid() { return (m_priority != -1); } const char* sinsp_decoder_syslog::get_severity_str() { if(m_severity >= sizeof(syslog_severity_strings) / sizeof(syslog_severity_strings[0])) { return ""; } else { return syslog_severity_strings[m_severity]; } } const char* sinsp_decoder_syslog::get_facility_str() { if(m_facility >= sizeof(syslog_facility_strings) / sizeof(syslog_facility_strings[0])) { return ""; } else { return syslog_facility_strings[m_facility]; } } void sinsp_decoder_syslog::decode_message(char *data, uint32_t len, char* pristr, uint32_t pristrlen) { if(len < pristrlen + 2 || pristrlen == 0) { m_priority = -1; return; } bool res = sinsp_numparser::tryparsed32_fast(pristr, pristrlen, &m_priority); if(!res) { m_priority = -1; return; } m_severity = m_priority & 0x07; m_facility = m_priority >> 3; m_msg.assign(data + pristrlen + 2, len - pristrlen - 2); m_inspector->protodecoder_register_reset(this); } bool sinsp_decoder_syslog::get_info_line(char** res) { m_infostr = string("syslog sev=") + get_severity_str() + " msg=" + m_msg; *res = (char*)m_infostr.c_str(); return (m_priority != -1); } sysdig-0.26.4/userspace/libsinsp/protodecoder.h000066400000000000000000000076751352731327100216070ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once /////////////////////////////////////////////////////////////////////////////// // The protocol decoder interface /////////////////////////////////////////////////////////////////////////////// class sinsp_protodecoder { public: sinsp_protodecoder(); virtual ~sinsp_protodecoder() { } // // Allocate a new decoder of the same type. // Every protodecoder plugin must implement this. // virtual sinsp_protodecoder* allocate_new() = 0; // // Allocate a new decoder of the same type. // Every protodecoder plugin must implement this. // virtual void init() = 0; // // Return the protocol decoder name // const string& get_name() { return m_name; } // // Called by the engine for each of the FDs that are added from proc // (or from the file) at the beginning of a capture. // virtual void on_fd_from_proc(sinsp_fdinfo_t* fdinfo) = 0; // // Called by the engine after an event has been received and parsed. // virtual void on_event(sinsp_evt* evt, sinsp_pd_callback_type etype) = 0; // // These are not part of on_event for performance reasons // virtual void on_read(sinsp_evt* evt, char *data, uint32_t len); virtual void on_write(sinsp_evt* evt, char *data, uint32_t len); virtual void on_reset(sinsp_evt* evt); // // Used by the engine to retrieve the info line for the last event. // Must return true if the line is valid. // virtual bool get_info_line(char** res) = 0; protected: // // Interface for the plugins // void register_event_callback(sinsp_pd_callback_type etype); void register_read_callback(sinsp_fdinfo_t* fdinfo); void register_write_callback(sinsp_fdinfo_t* fdinfo); void unregister_read_callback(sinsp_fdinfo_t* fdinfo); void unregister_write_callback(sinsp_fdinfo_t* fdinfo); string m_name; sinsp* m_inspector; private: void set_inspector(sinsp* inspector); friend class sinsp_protodecoder_list; }; /////////////////////////////////////////////////////////////////////////////// // Global class that stores the list of protocol decoders and offers // functions to work with it. /////////////////////////////////////////////////////////////////////////////// class sinsp_protodecoder_list { public: sinsp_protodecoder_list(); ~sinsp_protodecoder_list(); void add_protodecoder(sinsp_protodecoder* protodecoder); sinsp_protodecoder* new_protodecoder_from_name(const string& name, sinsp* inspector); private: vector m_decoders_list; }; /////////////////////////////////////////////////////////////////////////////// // Decoder classes // NOTE: these should be moved to a separate file but, since we have only one // for the moment, we keep it here /////////////////////////////////////////////////////////////////////////////// class sinsp_decoder_syslog : public sinsp_protodecoder { public: sinsp_decoder_syslog(); sinsp_protodecoder* allocate_new(); void init(); void on_fd_from_proc(sinsp_fdinfo_t* fdinfo); void on_event(sinsp_evt* evt, sinsp_pd_callback_type etype); void on_write(sinsp_evt* evt, char *data, uint32_t len); void on_reset(sinsp_evt* evt); bool get_info_line(char** res); bool is_data_valid(); const char* get_severity_str(); const char* get_facility_str(); int32_t m_priority; uint32_t m_facility; uint32_t m_severity; string m_msg; private: void decode_message(char *data, uint32_t len, char* pristr, uint32_t pristrlen); string m_infostr; }; sysdig-0.26.4/userspace/libsinsp/runc.cpp000066400000000000000000000047261352731327100204120ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "runc.h" #include #include "sinsp.h" #include "sinsp_int.h" namespace { const size_t CONTAINER_ID_LENGTH = 64; const size_t REPORTED_CONTAINER_ID_LENGTH = 12; const char* CONTAINER_ID_VALID_CHARACTERS = "0123456789abcdefABCDEF"; static_assert(REPORTED_CONTAINER_ID_LENGTH <= CONTAINER_ID_LENGTH, "Reported container ID length cannot be longer than actual length"); // check if cgroup ends with // If true, set to a truncated version of the id and return true. // Otherwise return false and leave container_id unchanged bool match_one_container_id(const std::string &cgroup, const std::string &prefix, const std::string &suffix, std::string &container_id) { size_t start_pos = cgroup.rfind(prefix); if (start_pos == std::string::npos) { return false; } start_pos += prefix.size(); size_t end_pos = cgroup.rfind(suffix); if (end_pos == std::string::npos) { return false; } if (end_pos - start_pos != CONTAINER_ID_LENGTH) { return false; } size_t invalid_ch_pos = cgroup.find_first_not_of(CONTAINER_ID_VALID_CHARACTERS, start_pos); if (invalid_ch_pos < CONTAINER_ID_LENGTH) { return false; } container_id = cgroup.substr(start_pos, REPORTED_CONTAINER_ID_LENGTH); return true; } bool match_container_id(const std::string &cgroup, const libsinsp::runc::cgroup_layout *layout, std::string &container_id) { for(size_t i = 0; layout[i].prefix && layout[i].suffix; ++i) { if(match_one_container_id(cgroup, layout[i].prefix, layout[i].suffix, container_id)) { return true; } } return false; } } namespace libsinsp { namespace runc { bool matches_runc_cgroups(const sinsp_threadinfo *tinfo, const cgroup_layout *layout, std::string &container_id) { for(const auto &it : tinfo->m_cgroups) { if(match_container_id(it.second, layout, container_id)) { return true; } } return false; } } }sysdig-0.26.4/userspace/libsinsp/runc.h000066400000000000000000000034001352731327100200430ustar00rootroot00000000000000/* Copyright (C) 2013-2019 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include class sinsp_threadinfo; namespace libsinsp { namespace runc { /// runc-based runtimes (Docker, containerd, CRI-O, probably others) use the same two cgroup layouts /// with slight variations: /// - non-systemd layout uses cgroups ending with .../ /// - systemd layout uses .../.scope /// where is always 64 hex digits (we report the first 12 as the container id). /// For non-systemd only CRI-O seems to use /crio-, while for systemd layout /// while all known container engines use a prefix like "docker-", "crio-" or "containerd-cri-". /// We can encode all these variants with a simple list of (prefix, suffix) pairs /// (the last one must be a pair of null pointers to mark the end of the array) struct cgroup_layout { const char* prefix; const char* suffix; }; /// If any of the cgroups of the thread in `tinfo` matches the `layout`, set `container_id` to the found id /// and return true. Otherwise, return false and leave `container_id` unchanged bool matches_runc_cgroups(const sinsp_threadinfo *tinfo, const cgroup_layout *layout, std::string &container_id); } } sysdig-0.26.4/userspace/libsinsp/settings.h000066400000000000000000000062161352731327100207440ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once // // This flag can be used to include unsupported or unrecognized sockets // in the fd tables. It's useful to debug close() leaks // #define INCLUDE_UNKNOWN_SOCKET_FDS // // Memory storage size for an entry in the event storage LIFO. // Events bigger than SP_EVT_BUF_SIZE won't be be stored in the LIFO. // #define SP_EVT_BUF_SIZE 4096 // // If defined, the filtering system is compiled // #define HAS_FILTERING #define HAS_CAPTURE_FILTERING // // Controls if assertions break execution or if they are just printed to the // log // #define ASSERT_TO_LOG // // Controls if the library collects internal performance stats. // #undef GATHER_INTERNAL_STATS // // Read timeout specified when doing scap_open // #define SCAP_TIMEOUT_MS 30 // // Max size that the thread table can reach // #define MAX_THREAD_TABLE_SIZE 131072 #define DEFAULT_THREAD_TABLE_SIZE 65536 // // Max size that the FD table of a process can reach // #define MAX_FD_TABLE_SIZE 4096 // // The time after an inactive thread is removed. // #define DEFAULT_THREAD_TIMEOUT_S 1800 // // How often the thread table is scanned for inactive threads // #define DEFAULT_INACTIVE_THREAD_SCAN_TIME_S 1200 // // How often the container table is scanned for inactive containers // #define DEFAULT_INACTIVE_CONTAINER_SCAN_TIME_S 30 // // Enables Lua chisel scripts support // #define HAS_CHISELS // // Relative path to chisels // #define CHISELS_INSTALLATION_DIR "/share/sysdig/chisels" // // Default snaplen // #define DEFAULT_SNAPLEN 80 // // Maximum user event buffer size // #define MAX_USER_EVT_BUFFER 65536 // // Size the user event buffer is brought back once in a while // #define MIN_USER_EVT_BUFFER 256 // // Is csysdig functionality included? // #define CSYSDIG #ifdef _WIN32 #define NOCURSESUI #endif // // Name of the device used for tracer injection // #define USER_EVT_DEVICE_NAME "/dev/null" // // The time after which a clone should be considered stale // #define CLONE_STALE_TIME_NS 2000000000 // // For internal use // #define FALCOBL_FULL_PROCESSING // // Port range to enable larger snaplen on // #define DEFAULT_INCREASE_SNAPLEN_PORT_RANGE {0, 0} // // FD class customized with the storage we need // #ifdef HAS_ANALYZER #include "analyzer_settings.h" #else template class sinsp_fdinfo; typedef sinsp_fdinfo sinsp_fdinfo_t; #endif // HAS_ANALYZER // Max JSON we can parse from docker API or others // Added because older docker versions have a bug that causes // very big JSONs returned by container inspect call static const unsigned MAX_JSON_SIZE_B = 500 * 1024; // 500 kiB sysdig-0.26.4/userspace/libsinsp/sinsp.cpp000066400000000000000000001510561352731327100205760ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #ifndef _WIN32 #include #include #include #include #include #include #endif // _WIN32 #include "sinsp.h" #include "sinsp_int.h" #include "sinsp_auth.h" #include "filter.h" #include "filterchecks.h" #include "chisel.h" #include "cyclewriter.h" #include "protodecoder.h" #include "dns_manager.h" #ifndef CYGWING_AGENT #include "k8s_api_handler.h" #ifdef HAS_CAPTURE #include #endif #endif #ifdef HAS_ANALYZER #include "analyzer_int.h" #include "analyzer.h" #include "tracer_emitter.h" #endif #ifdef HAS_CHISELS extern vector* g_chisel_dirs; #endif void on_new_entry_from_proc(void* context, scap_t* handle, int64_t tid, scap_threadinfo* tinfo, scap_fdinfo* fdinfo); /////////////////////////////////////////////////////////////////////////////// // sinsp implementation /////////////////////////////////////////////////////////////////////////////// sinsp::sinsp() : m_evt(this), m_lastevent_ts(0), m_container_manager(this), m_suppressed_comms() { #if !defined(CYGWING_AGENT) && defined(HAS_CAPTURE) // used by mesos and container_manager curl_global_init(CURL_GLOBAL_DEFAULT); #endif m_h = NULL; m_parser = NULL; m_dumper = NULL; m_is_dumping = false; m_metaevt = NULL; m_meinfo.m_piscapevt = NULL; m_network_interfaces = NULL; m_parser = new sinsp_parser(this); m_thread_manager = new sinsp_thread_manager(this); m_max_thread_table_size = DEFAULT_THREAD_TABLE_SIZE; m_max_fdtable_size = MAX_FD_TABLE_SIZE; m_thread_timeout_ns = DEFAULT_THREAD_TIMEOUT_S * ONE_SECOND_IN_NS; m_inactive_thread_scan_time_ns = DEFAULT_INACTIVE_THREAD_SCAN_TIME_S * ONE_SECOND_IN_NS; m_inactive_container_scan_time_ns = DEFAULT_INACTIVE_CONTAINER_SCAN_TIME_S * ONE_SECOND_IN_NS; m_cycle_writer = NULL; m_write_cycling = false; #ifdef HAS_ANALYZER m_analyzer = NULL; #endif #ifdef HAS_FILTERING m_filter = NULL; m_evttype_filter = NULL; #endif m_fds_to_remove = new vector; m_machine_info = NULL; #ifdef SIMULATE_DROP_MODE m_isdropping = false; #endif m_n_proc_lookups = 0; m_n_proc_lookups_duration_ns = 0; m_snaplen = DEFAULT_SNAPLEN; m_buffer_format = sinsp_evt::PF_NORMAL; m_input_fd = 0; m_bpf = false; m_isdebug_enabled = false; m_isfatfile_enabled = false; m_isinternal_events_enabled = false; m_hostname_and_port_resolution_enabled = false; m_output_time_flag = 'h'; m_max_evt_output_len = 0; m_filesize = -1; m_track_tracers_state = false; m_import_users = true; m_next_flush_time_ns = 0; m_last_procrequest_tod = 0; m_get_procs_cpu_from_driver = false; m_is_tracers_capture_enabled = false; m_file_start_offset = 0; m_flush_memory_dump = false; m_next_stats_print_time_ns = 0; m_large_envs_enabled = false; m_increased_snaplen_port_range = DEFAULT_INCREASE_SNAPLEN_PORT_RANGE; m_statsd_port = -1; // Unless the cmd line arg "-pc" or "-pcontainer" is supplied this is false m_print_container_data = false; #if defined(HAS_CAPTURE) m_sysdig_pid = getpid(); #endif uint32_t evlen = sizeof(scap_evt) + 2 * sizeof(uint16_t) + 2 * sizeof(uint64_t); m_meinfo.m_piscapevt = (scap_evt*)new char[evlen]; m_meinfo.m_piscapevt->type = PPME_PROCINFO_E; m_meinfo.m_piscapevt->len = evlen; m_meinfo.m_piscapevt->nparams = 2; uint16_t* lens = (uint16_t*)((char *)m_meinfo.m_piscapevt + sizeof(struct ppm_evt_hdr)); lens[0] = 8; lens[1] = 8; m_meinfo.m_piscapevt_vals = (uint64_t*)(lens + 2); m_meinfo.m_pievt.m_inspector = this; m_meinfo.m_pievt.m_info = &(g_infotables.m_event_info[PPME_SYSDIGEVENT_X]); m_meinfo.m_pievt.m_pevt = NULL; m_meinfo.m_pievt.m_cpuid = 0; m_meinfo.m_pievt.m_evtnum = 0; m_meinfo.m_pievt.m_pevt = m_meinfo.m_piscapevt; m_meinfo.m_pievt.m_fdinfo = NULL; m_meinfo.m_n_procinfo_evts = 0; m_meta_event_callback = NULL; m_meta_event_callback_data = NULL; #ifndef CYGWING_AGENT m_k8s_client = NULL; m_k8s_last_watch_time_ns = 0; m_k8s_client = NULL; m_k8s_api_server = NULL; m_k8s_api_cert = NULL; m_mesos_client = NULL; m_mesos_last_watch_time_ns = 0; #endif m_filter_proc_table_when_saving = false; } sinsp::~sinsp() { close(); if(m_fds_to_remove) { delete m_fds_to_remove; } if(m_parser) { delete m_parser; m_parser = NULL; } if(m_thread_manager) { delete m_thread_manager; m_thread_manager = NULL; } if(m_cycle_writer) { delete m_cycle_writer; m_cycle_writer = NULL; } if(m_meinfo.m_piscapevt) { delete[] m_meinfo.m_piscapevt; } m_container_manager.cleanup(); #ifndef CYGWING_AGENT delete m_k8s_client; delete m_k8s_api_server; delete m_k8s_api_cert; delete m_mesos_client; #ifdef HAS_CAPTURE curl_global_cleanup(); sinsp_dns_manager::get().cleanup(); #endif #endif } void sinsp::add_protodecoders() { m_parser->add_protodecoder("syslog"); } void sinsp::filter_proc_table_when_saving(bool filter) { m_filter_proc_table_when_saving = filter; if(m_h != NULL) { scap_set_refresh_proc_table_when_saving(m_h, !filter); } } void sinsp::enable_tracers_capture() { #if defined(HAS_CAPTURE) && ! defined(CYGWING_AGENT) if(!m_is_tracers_capture_enabled) { if(is_live() && m_h != NULL) { if(scap_enable_tracers_capture(m_h) != SCAP_SUCCESS) { throw sinsp_exception("error enabling tracers capture"); } } m_is_tracers_capture_enabled = true; } #endif } void sinsp::enable_page_faults() { #if defined(HAS_CAPTURE) && ! defined(CYGWING_AGENT) if(is_live() && m_h != NULL) { if(scap_enable_page_faults(m_h) != SCAP_SUCCESS) { throw sinsp_exception("error enabling page_faults"); } } #endif } void sinsp::init() { // // Retrieve machine information // m_machine_info = scap_get_machine_info(m_h); if(m_machine_info != NULL) { m_num_cpus = m_machine_info->num_cpus; } else { ASSERT(false); m_num_cpus = 0; } // // Attach the protocol decoders // #ifndef HAS_ANALYZER add_protodecoders(); #endif // // Allocate the cycle writer // if(m_cycle_writer) { delete m_cycle_writer; m_cycle_writer = NULL; } m_cycle_writer = new cycle_writer(is_live()); // // Basic inits // #ifdef GATHER_INTERNAL_STATS m_stats.clear(); #endif m_nevts = 0; m_tid_to_remove = -1; m_lastevent_ts = 0; #ifdef HAS_FILTERING m_firstevent_ts = 0; #endif m_fds_to_remove->clear(); m_n_proc_lookups = 0; m_n_proc_lookups_duration_ns = 0; // // Return the tracers to the pool and clear the tracers list // for(auto it = m_partial_tracers_list.begin(); it != m_partial_tracers_list.end(); ++it) { m_partial_tracers_pool->push(*it); } m_partial_tracers_list.clear(); // // If we're reading from file, we try to pre-parse the container events before // importing the thread table, so that thread table filtering will work with // container filters // if(is_capture()) { uint64_t off = scap_ftell(m_h); scap_evt* pevent; uint16_t pcpuid; uint32_t ncnt = 0; // // Count how many container events we have // while(true) { int32_t res = scap_next(m_h, &pevent, &pcpuid); if(res == SCAP_SUCCESS) { if((pevent->type != PPME_CONTAINER_E) && (pevent->type != PPME_CONTAINER_JSON_E)) { break; } else { ncnt++; continue; } } else { break; } } #ifdef HAS_ANALYZER // // Notify the analyzer that we're starting // if(m_analyzer) { m_analyzer->on_capture_start(); } #endif // // Rewind, reset the event count, and consume the exact number of events // scap_fseek(m_h, off); scap_event_reset_count(m_h); for(uint32_t j = 0; j < ncnt; j++) { sinsp_evt* tevt; next(&tevt); } } if(is_capture() || m_filter_proc_table_when_saving == true) { import_thread_table(); } import_ifaddr_list(); import_user_list(); // // Scan the list to create the proper parent/child dependencies // m_thread_manager->create_child_dependencies(); // // Scan the list to fix the direction of the sockets // m_thread_manager->fix_sockets_coming_from_proc(); #ifdef HAS_ANALYZER // // Notify the analyzer that we're starting // if(m_analyzer) { m_analyzer->on_capture_start(); } #endif // // If m_snaplen was modified, we set snaplen now // if(m_snaplen != DEFAULT_SNAPLEN) { set_snaplen(m_snaplen); } // // If the port range for increased snaplen was modified, set it now // if(increased_snaplen_port_range_set()) { set_fullcapture_port_range(m_increased_snaplen_port_range.range_start, m_increased_snaplen_port_range.range_end); } // // If the statsd port was modified, push it to the kernel now. // if(m_statsd_port != -1) { set_statsd_port(m_statsd_port); } #if defined(HAS_CAPTURE) if(m_mode == SCAP_MODE_LIVE) { if(scap_getpid_global(m_h, &m_sysdig_pid) != SCAP_SUCCESS) { ASSERT(false); } } #endif } void sinsp::set_import_users(bool import_users) { m_import_users = import_users; } void sinsp::open(uint32_t timeout_ms) { char error[SCAP_LASTERR_SIZE]; g_logger.log("starting live capture"); // // Reset the thread manager // m_thread_manager->clear(); // // Start the capture // m_mode = SCAP_MODE_LIVE; scap_open_args oargs; oargs.mode = SCAP_MODE_LIVE; oargs.fname = NULL; oargs.proc_callback = NULL; oargs.proc_callback_context = NULL; if(!m_filter_proc_table_when_saving) { oargs.proc_callback = ::on_new_entry_from_proc; oargs.proc_callback_context = this; } oargs.import_users = m_import_users; add_suppressed_comms(oargs); if(m_bpf) { oargs.bpf_probe = m_bpf_probe.c_str(); } else { oargs.bpf_probe = NULL; } add_suppressed_comms(oargs); int32_t scap_rc; m_h = scap_open(oargs, error, &scap_rc); if(m_h == NULL) { throw sinsp_exception(error, scap_rc); } scap_set_refresh_proc_table_when_saving(m_h, !m_filter_proc_table_when_saving); init(); } void sinsp::open_nodriver() { char error[SCAP_LASTERR_SIZE]; g_logger.log("starting optimized sinsp"); // // Reset the thread manager // m_thread_manager->clear(); // // Start the capture // m_mode = SCAP_MODE_NODRIVER; scap_open_args oargs; oargs.mode = SCAP_MODE_NODRIVER; oargs.fname = NULL; oargs.proc_callback = NULL; oargs.proc_callback_context = NULL; if(!m_filter_proc_table_when_saving) { oargs.proc_callback = ::on_new_entry_from_proc; oargs.proc_callback_context = this; } oargs.import_users = m_import_users; int32_t scap_rc; m_h = scap_open(oargs, error, &scap_rc); if(m_h == NULL) { throw sinsp_exception(error, scap_rc); } scap_set_refresh_proc_table_when_saving(m_h, !m_filter_proc_table_when_saving); init(); } int64_t sinsp::get_file_size(const std::string& fname, char *error) { static string err_str = "Could not determine capture file size: "; std::string errdesc; #ifdef _WIN32 LARGE_INTEGER li = { 0 }; HANDLE fh = CreateFile(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); if (fh != INVALID_HANDLE_VALUE) { if (0 != GetFileSizeEx(fh, &li)) { CloseHandle(fh); return li.QuadPart; } errdesc = get_error_desc(err_str); CloseHandle(fh); } #else struct stat st; if (0 == stat(fname.c_str(), &st)) { return st.st_size; } #endif if(errdesc.empty()) errdesc = get_error_desc(err_str); strncpy(error, errdesc.c_str(), errdesc.size() > SCAP_LASTERR_SIZE ? SCAP_LASTERR_SIZE : errdesc.size()); return -1; } void sinsp::set_simpledriver_mode() { if(scap_enable_simpledriver_mode(m_h) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_h)); } } unsigned sinsp::m_num_possible_cpus = 0; unsigned sinsp::num_possible_cpus() { if(m_num_possible_cpus == 0) { m_num_possible_cpus = read_num_possible_cpus(); if(m_num_possible_cpus == 0) { g_logger.log("Unable to read num_possible_cpus, falling back to 128", sinsp_logger::SEV_WARNING); m_num_possible_cpus = 128; } } return m_num_possible_cpus; } vector sinsp::get_n_tracepoint_hit() { vector ret(num_possible_cpus(), 0); if(scap_get_n_tracepoint_hit(m_h, ret.data()) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_h)); } return ret; } std::string sinsp::get_error_desc(const std::string& msg) { #ifdef _WIN32 DWORD err_no = GetLastError(); // first, so error is not wiped out by intermediate calls std::string errstr = msg; DWORD flg = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; LPTSTR msg_buf = 0; if(FormatMessageA(flg, 0, err_no, 0, (LPTSTR)&msg_buf, 0, NULL)) if(msg_buf) { errstr.append(msg_buf, strlen(msg_buf)); LocalFree(msg_buf); } #else char* msg_buf = strerror(errno); // first, so error is not wiped out by intermediate calls std::string errstr = msg; if(msg_buf) { errstr.append(msg_buf, strlen(msg_buf)); } #endif return errstr; } void sinsp::open_int() { char error[SCAP_LASTERR_SIZE] = {0}; // // Reset the thread manager // m_thread_manager->clear(); // // Start the capture // m_mode = SCAP_MODE_CAPTURE; scap_open_args oargs; oargs.mode = SCAP_MODE_CAPTURE; if(m_input_fd != 0) { oargs.fd = m_input_fd; } else { oargs.fd = 0; oargs.fname = m_input_filename.c_str(); } oargs.proc_callback = NULL; oargs.proc_callback_context = NULL; oargs.import_users = m_import_users; if(m_file_start_offset != 0) { oargs.start_offset = m_file_start_offset; } else { oargs.start_offset = 0; } add_suppressed_comms(oargs); int32_t scap_rc; m_h = scap_open(oargs, error, &scap_rc); if(m_h == NULL) { throw sinsp_exception(error, scap_rc); } if(m_input_fd != 0) { // We can't get a reliable filesize m_filesize = 0; } else { m_filesize = get_file_size(m_input_filename, error); if(m_filesize < 0) { throw sinsp_exception(error); } } init(); } void sinsp::open(const std::string &filename) { if(filename.empty()) { open(); return; } m_input_filename = filename; g_logger.log("starting offline capture"); open_int(); } void sinsp::fdopen(int fd) { m_input_fd = fd; g_logger.log("starting offline capture"); open_int(); } void sinsp::close() { if(m_h) { scap_close(m_h); m_h = NULL; } if(NULL != m_dumper) { scap_dump_close(m_dumper); m_dumper = NULL; } m_is_dumping = false; if(NULL != m_network_interfaces) { delete m_network_interfaces; m_network_interfaces = NULL; } #ifdef HAS_FILTERING if(m_filter != NULL) { delete m_filter; m_filter = NULL; } if(m_evttype_filter != NULL) { delete m_evttype_filter; m_evttype_filter = NULL; } #endif } void sinsp::autodump_start(const string& dump_filename, bool compress) { if(NULL == m_h) { throw sinsp_exception("inspector not opened yet"); } if(compress) { m_dumper = scap_dump_open(m_h, dump_filename.c_str(), SCAP_COMPRESSION_GZIP, false); } else { m_dumper = scap_dump_open(m_h, dump_filename.c_str(), SCAP_COMPRESSION_NONE, false); } m_is_dumping = true; if(NULL == m_dumper) { throw sinsp_exception(scap_getlasterr(m_h)); } m_container_manager.dump_containers(m_dumper); } void sinsp::autodump_next_file() { autodump_stop(); autodump_start(m_cycle_writer->get_current_file_name(), m_compress); } void sinsp::autodump_stop() { if(NULL == m_h) { throw sinsp_exception("inspector not opened yet"); } if(m_dumper != NULL) { scap_dump_close(m_dumper); m_dumper = NULL; } m_is_dumping = false; } void sinsp::on_new_entry_from_proc(void* context, scap_t* handle, int64_t tid, scap_threadinfo* tinfo, scap_fdinfo* fdinfo) { ASSERT(tinfo != NULL); m_h = handle; // // Retrieve machine information if we don't have it yet // { m_machine_info = scap_get_machine_info(handle); if(m_machine_info != NULL) { m_num_cpus = m_machine_info->num_cpus; } else { ASSERT(false); m_num_cpus = 0; } } // // Add the thread or FD // if(fdinfo == NULL) { bool thread_added = false; sinsp_threadinfo* newti = new sinsp_threadinfo(this); newti->init(tinfo); if(is_nodriver()) { auto sinsp_tinfo = find_thread(tid, true); if(sinsp_tinfo == nullptr || newti->m_clone_ts > sinsp_tinfo->m_clone_ts) { thread_added = m_thread_manager->add_thread(newti, true); } } else { thread_added = m_thread_manager->add_thread(newti, true); } if (!thread_added) { delete newti; } } else { auto sinsp_tinfo = find_thread(tid, true); if(!sinsp_tinfo) { sinsp_threadinfo* newti = new sinsp_threadinfo(this); newti->init(tinfo); if (!m_thread_manager->add_thread(newti, true)) { ASSERT(false); delete newti; return; } sinsp_tinfo = find_thread(tid, true); if (!sinsp_tinfo) { ASSERT(false); return; } } sinsp_fdinfo_t sinsp_fdinfo; sinsp_tinfo->add_fd_from_scap(fdinfo, &sinsp_fdinfo); } } void on_new_entry_from_proc(void* context, scap_t* handle, int64_t tid, scap_threadinfo* tinfo, scap_fdinfo* fdinfo) { sinsp* _this = (sinsp*)context; _this->on_new_entry_from_proc(context, handle, tid, tinfo, fdinfo); } void sinsp::import_thread_table() { scap_threadinfo *pi; scap_threadinfo *tpi; scap_threadinfo *table = scap_get_proc_table(m_h); // // Scan the scap table and add the threads to our list // HASH_ITER(hh, table, pi, tpi) { sinsp_threadinfo* newti = new sinsp_threadinfo(this); newti->init(pi); m_thread_manager->add_thread(newti, true); } } void sinsp::import_ifaddr_list() { m_network_interfaces = new sinsp_network_interfaces(this); m_network_interfaces->import_interfaces(scap_get_ifaddr_list(m_h)); } sinsp_network_interfaces* sinsp::get_ifaddr_list() { return m_network_interfaces; } void sinsp::import_user_list() { uint32_t j; scap_userlist* ul = scap_get_user_list(m_h); if(ul) { for(j = 0; j < ul->nusers; j++) { m_userlist[ul->users[j].uid] = &(ul->users[j]); } for(j = 0; j < ul->ngroups; j++) { m_grouplist[ul->groups[j].gid] = &(ul->groups[j]); } } } void sinsp::import_ipv4_interface(const sinsp_ipv4_ifinfo& ifinfo) { ASSERT(m_network_interfaces); m_network_interfaces->import_ipv4_interface(ifinfo); } void sinsp::refresh_ifaddr_list() { #ifdef HAS_CAPTURE if(!is_capture()) { ASSERT(m_network_interfaces); scap_refresh_iflist(m_h); m_network_interfaces->clear(); m_network_interfaces->import_interfaces(scap_get_ifaddr_list(m_h)); } #endif } bool should_drop(sinsp_evt *evt, bool* stopped, bool* switched); void sinsp::add_meta_event(sinsp_evt *metaevt) { m_metaevt = metaevt; } void sinsp::add_meta_event_callback(meta_event_callback cback, void* data) { m_meta_event_callback = cback; m_meta_event_callback_data = data; } void sinsp::remove_meta_event_callback() { m_meta_event_callback = NULL; } void schedule_next_threadinfo_evt(sinsp* _this, void* data) { sinsp_proc_metainfo* mei = (sinsp_proc_metainfo*)data; ASSERT(mei->m_pli != NULL); while(true) { ASSERT(mei->m_cur_procinfo_evt <= (int32_t)mei->m_n_procinfo_evts); ppm_proc_info* pi = &(mei->m_pli->entries[mei->m_cur_procinfo_evt]); if(mei->m_cur_procinfo_evt >= 0) { mei->m_piscapevt->tid = pi->pid; mei->m_piscapevt_vals[0] = pi->utime; mei->m_piscapevt_vals[1] = pi->stime; } mei->m_cur_procinfo_evt++; if(mei->m_cur_procinfo_evt < (int32_t)mei->m_n_procinfo_evts) { if(pi->utime == 0 && pi->stime == 0) { continue; } _this->add_meta_event(&mei->m_pievt); } else if(mei->m_cur_procinfo_evt == (int32_t)mei->m_n_procinfo_evts) { _this->add_meta_event(mei->m_next_evt); } break; } } void sinsp::restart_capture_at_filepos(uint64_t filepos) { // // Backup a couple of settings // uint64_t evtnum = m_nevts; string filterstring = m_filterstring; // // Close and reopen the capture // m_file_start_offset = filepos; close(); open_int(); // // Set again the backuped settings // m_evt.m_evtnum = evtnum; m_nevts = evtnum; if(filterstring != "") { set_filter(filterstring); } } int32_t sinsp::next(OUT sinsp_evt **puevt) { sinsp_evt* evt; int32_t res; // // Check if there are fake cpu events to events // if(m_metaevt != NULL) { res = SCAP_SUCCESS; evt = m_metaevt; m_metaevt = NULL; if(m_meta_event_callback != NULL) { m_meta_event_callback(this, m_meta_event_callback_data); } } else if (m_pending_container_evts.try_pop(m_container_evt)) { res = SCAP_SUCCESS; evt = m_container_evt.get(); } else { evt = &m_evt; // // Reset previous event's decoders if required // if(m_decoders_reset_list.size() != 0) { vector::iterator it; for(it = m_decoders_reset_list.begin(); it != m_decoders_reset_list.end(); ++it) { (*it)->on_reset(evt); } m_decoders_reset_list.clear(); } // // Get the event from libscap // res = scap_next(m_h, &(evt->m_pevt), &(evt->m_cpuid)); if(res != SCAP_SUCCESS) { if(res == SCAP_TIMEOUT) { #ifdef HAS_ANALYZER if(m_analyzer) { m_analyzer->process_event(NULL, analyzer_emitter::DF_TIMEOUT); } #endif *puevt = NULL; return res; } else if(res == SCAP_EOF) { #ifdef HAS_ANALYZER if(m_analyzer) { m_analyzer->process_event(NULL, analyzer_emitter::DF_EOF); } #endif } else if(res == SCAP_UNEXPECTED_BLOCK) { uint64_t filepos = scap_ftell(m_h) - scap_get_unexpected_block_readsize(m_h); restart_capture_at_filepos(filepos); return SCAP_TIMEOUT; } else { m_lasterr = scap_getlasterr(m_h); } return res; } } uint64_t ts = evt->get_ts(); if(m_firstevent_ts == 0 && evt->m_pevt->type != PPME_CONTAINER_JSON_E) { m_firstevent_ts = ts; } // // If required, retrieve the processes cpu from the kernel // if(m_get_procs_cpu_from_driver && is_live()) { if(ts > m_next_flush_time_ns) { if(m_next_flush_time_ns != 0) { struct timeval tod; gettimeofday(&tod, NULL); uint64_t procrequest_tod = (uint64_t)tod.tv_sec * 1000000000 + tod.tv_usec * 1000; if(procrequest_tod - m_last_procrequest_tod > ONE_SECOND_IN_NS / 2) { m_last_procrequest_tod = procrequest_tod; m_next_flush_time_ns = ts - (ts % ONE_SECOND_IN_NS) + ONE_SECOND_IN_NS; m_meinfo.m_pli = scap_get_threadlist(m_h); if(m_meinfo.m_pli == NULL) { throw sinsp_exception(string("scap error: ") + scap_getlasterr(m_h)); } m_meinfo.m_n_procinfo_evts = m_meinfo.m_pli->n_entries; if(m_meinfo.m_n_procinfo_evts > 0) { m_meinfo.m_cur_procinfo_evt = -1; m_meinfo.m_piscapevt->ts = m_next_flush_time_ns - (ONE_SECOND_IN_NS + 1); m_meinfo.m_next_evt = &m_evt; add_meta_event_callback(&schedule_next_threadinfo_evt, &m_meinfo); schedule_next_threadinfo_evt(this, &m_meinfo); } return SCAP_TIMEOUT; } } m_next_flush_time_ns = ts - (ts % ONE_SECOND_IN_NS) + ONE_SECOND_IN_NS; } } // // Store a couple of values that we'll need later inside the event. // m_nevts++; evt->m_evtnum = m_nevts; m_lastevent_ts = ts; #ifndef HAS_ANALYZER // // Deleayed removal of threads from the thread table, so that // things like exit() or close() can be parsed. // We only do this if the analyzer is not enabled, because the analyzer // needs the process at the end of the sample and will take care of deleting // it. // if(m_tid_to_remove != -1) { remove_thread(m_tid_to_remove, false); m_tid_to_remove = -1; } if(is_debug_enabled() && is_live()) { if(ts > m_next_stats_print_time_ns) { if(m_next_stats_print_time_ns) { scap_stats stats; get_capture_stats(&stats); g_logger.format(sinsp_logger::SEV_DEBUG, "n_evts:%" PRIu64 " n_drops:%" PRIu64 " n_drops_buffer:%" PRIu64 " n_drops_pf:%" PRIu64 " n_drops_bug:%" PRIu64, stats.n_evts, stats.n_drops, stats.n_drops_buffer, stats.n_drops_pf, stats.n_drops_bug); } m_next_stats_print_time_ns = ts - (ts % ONE_SECOND_IN_NS) + ONE_SECOND_IN_NS; } } // // Run the periodic connection and thread table cleanup // if(!is_capture()) { m_thread_manager->remove_inactive_threads(); m_container_manager.remove_inactive_containers(); update_k8s_state(); if(m_mesos_client) { update_mesos_state(); } } #endif // HAS_ANALYZER // // Deleayed removal of the fd, so that // things like exit() or close() can be parsed. // uint32_t nfdr = (uint32_t)m_fds_to_remove->size(); if(nfdr != 0) { sinsp_threadinfo* ptinfo = get_thread(m_tid_of_fd_to_remove, true, true); if(!ptinfo) { ASSERT(false); return res; } for(uint32_t j = 0; j < nfdr; j++) { ptinfo->remove_fd(m_fds_to_remove->at(j)); } m_fds_to_remove->clear(); } #ifdef SIMULATE_DROP_MODE bool sd = false; bool sw = false; if(m_analyzer) { m_analyzer->m_configuration->set_analyzer_sample_len_ns(500000000); } sd = should_drop(evt, &m_isdropping, &sw); #endif // // Run the state engine // #ifdef SIMULATE_DROP_MODE if(!sd || m_isdropping) { m_parser->process_event(evt); } if(sd && !m_isdropping) { *evt = NULL; return SCAP_TIMEOUT; } #else m_parser->process_event(evt); #endif // // If needed, dump the event to file // if(NULL != m_dumper) { #if defined(HAS_FILTERING) && defined(HAS_CAPTURE_FILTERING) scap_dump_flags dflags; bool do_drop; dflags = evt->get_dump_flags(&do_drop); if(do_drop) { *puevt = evt; return SCAP_TIMEOUT; } #endif if(m_write_cycling) { switch(m_cycle_writer->consider(evt)) { case cycle_writer::NEWFILE: autodump_next_file(); break; case cycle_writer::DOQUIT: stop_capture(); return SCAP_EOF; break; case cycle_writer::SAMEFILE: // do nothing. break; } } scap_evt* pdevt = (evt->m_poriginal_evt)? evt->m_poriginal_evt : evt->m_pevt; res = scap_dump(m_h, m_dumper, pdevt, evt->m_cpuid, dflags); if(SCAP_SUCCESS != res) { throw sinsp_exception(scap_getlasterr(m_h)); } } #if defined(HAS_FILTERING) && defined(HAS_CAPTURE_FILTERING) if(evt->m_filtered_out) { ppm_event_category cat = evt->get_info_category(); // Skip the event, unless we're in internal events // mode and the category of this event is internal. if(!(m_isinternal_events_enabled && (cat & EC_INTERNAL))) { *puevt = evt; return SCAP_TIMEOUT; } } #endif // // Run the analysis engine // #ifdef HAS_ANALYZER if(m_analyzer) { #ifdef SIMULATE_DROP_MODE if(!sd || m_isdropping || sw) { if(m_isdropping) { m_analyzer->process_event(evt, analyzer_emitter::DF_FORCE_FLUSH); } else if(sw) { m_analyzer->process_event(evt, analyzer_emitter::DF_FORCE_FLUSH_BUT_DONT_EMIT); } else { m_analyzer->process_event(evt, analyzer_emitter::DF_FORCE_NOFLUSH); } } #else // SIMULATE_DROP_MODE m_analyzer->process_event(evt, analyzer_emitter::DF_NONE); #endif // SIMULATE_DROP_MODE } #endif // Clean parse related event data after analyzer did its parsing too m_parser->event_cleanup(evt); // // Update the last event time for this thread // if(evt->m_tinfo && evt->get_type() != PPME_SCHEDSWITCH_1_E && evt->get_type() != PPME_SCHEDSWITCH_6_E) { evt->m_tinfo->m_prevevent_ts = evt->m_tinfo->m_lastevent_ts; evt->m_tinfo->m_lastevent_ts = m_lastevent_ts; } // // Done // *puevt = evt; return res; } uint64_t sinsp::get_num_events() { if(m_h) { return scap_event_get_num(m_h); } else { return 0; } } sinsp_threadinfo* sinsp::find_thread_test(int64_t tid, bool lookup_only) { // TODO: we pay the refcount manipulation price here return &*find_thread(tid, lookup_only); } sinsp_threadinfo* sinsp::get_thread(int64_t tid, bool query_os_if_not_found, bool lookup_only) { // TODO: we pay the refcount manipulation price here return &*get_thread_ref(tid, query_os_if_not_found, lookup_only); } threadinfo_map_t::ptr_t sinsp::get_thread_ref(int64_t tid, bool query_os_if_not_found, bool lookup_only) { auto sinsp_proc = find_thread(tid, lookup_only); if(!sinsp_proc && query_os_if_not_found && (m_thread_manager->m_threadtable.size() < m_max_thread_table_size #if defined(HAS_CAPTURE) || tid == m_sysdig_pid #endif )) { // Certain code paths can lead to this point from scap_open() (incomplete example: // scap_proc_scan_proc_dir() -> resolve_container() -> get_env()). Adding a // defensive check here to protect both, callers of get_env and get_thread. if (!m_h) { g_logger.format(sinsp_logger::SEV_INFO, "%s: Unable to complete for tid=%" PRIu64 ": sinsp::scap_t* is uninitialized", __func__, tid); return NULL; } scap_threadinfo* scap_proc = NULL; sinsp_threadinfo* newti = new sinsp_threadinfo(this); m_n_proc_lookups++; if(m_n_proc_lookups == m_max_n_proc_lookups) { g_logger.format(sinsp_logger::SEV_INFO, "Reached max process lookup number, duration=%" PRIu64 "ms", m_n_proc_lookups_duration_ns / 1000000); } if(m_max_n_proc_lookups < 0 || m_n_proc_lookups <= m_max_n_proc_lookups) { #ifdef HAS_ANALYZER tracer_emitter("sinsp_proc_lookup"); #endif bool scan_sockets = false; if(m_max_n_proc_socket_lookups < 0 || m_n_proc_lookups <= m_max_n_proc_socket_lookups) { scan_sockets = true; if(m_n_proc_lookups == m_max_n_proc_socket_lookups) { g_logger.format(sinsp_logger::SEV_INFO, "Reached max socket lookup number, tid=%" PRIu64 ", duration=%" PRIu64 "ms", tid, m_n_proc_lookups_duration_ns / 1000000); } } #ifdef HAS_ANALYZER uint64_t ts = sinsp_utils::get_current_time_ns(); #endif scap_proc = scap_proc_get(m_h, tid, scan_sockets); #ifdef HAS_ANALYZER m_n_proc_lookups_duration_ns += sinsp_utils::get_current_time_ns() - ts; #endif } if(scap_proc) { newti->init(scap_proc); scap_proc_free(m_h, scap_proc); } else { // // Add a fake entry to avoid a continuous lookup // newti->m_tid = tid; newti->m_pid = tid; newti->m_ptid = -1; newti->m_comm = ""; newti->m_exe = ""; newti->m_uid = 0xffffffff; newti->m_gid = 0xffffffff; newti->m_nchilds = 0; newti->m_loginuid = 0xffffffff; } // // Since this thread is created out of thin air, we need to // properly set its reference count, by scanning the table // m_thread_manager->m_threadtable.loop([&] (sinsp_threadinfo& tinfo) { if(tinfo.m_pid == tid) { newti->m_nchilds++; } return true; }); // // Done. Add the new thread to the list. // m_thread_manager->add_thread(newti, false); sinsp_proc = find_thread(tid, lookup_only); } return sinsp_proc; } sinsp_threadinfo* sinsp::get_thread(int64_t tid) { return get_thread(tid, false, true); } bool sinsp::add_thread(const sinsp_threadinfo *ptinfo) { return m_thread_manager->add_thread((sinsp_threadinfo*)ptinfo, false); } void sinsp::remove_thread(int64_t tid, bool force) { m_thread_manager->remove_thread(tid, force); } bool sinsp::suppress_events_comm(const std::string &comm) { if(m_suppressed_comms.size() >= SCAP_MAX_SUPPRESSED_COMMS) { return false; } m_suppressed_comms.insert(comm); if(m_h) { if (scap_suppress_events_comm(m_h, comm.c_str()) != SCAP_SUCCESS) { return false; } } return true; } bool sinsp::check_suppressed(int64_t tid) { return scap_check_suppressed_tid(m_h, tid); } void sinsp::add_suppressed_comms(scap_open_args &oargs) { uint32_t i = 0; // Note--using direct pointers to values in // m_suppressed_comms. This is ok given that a scap_open() // will immediately follow after which the args won't be used. for(auto &comm : m_suppressed_comms) { oargs.suppressed_comms[i++] = comm.c_str(); } oargs.suppressed_comms[i++] = NULL; } void sinsp::set_query_docker_image_info(bool query_image_info) { m_container_manager.set_query_docker_image_info(query_image_info); } void sinsp::set_cri_extra_queries(bool extra_queries) { m_container_manager.set_cri_extra_queries(extra_queries); } void sinsp::set_cri_socket_path(const std::string& path) { m_container_manager.set_cri_socket_path(path); } void sinsp::set_cri_timeout(int64_t timeout_ms) { m_container_manager.set_cri_timeout(timeout_ms); } void sinsp::set_snaplen(uint32_t snaplen) { // // If set_snaplen is called before opening of the inspector, // we register the value to be set after its initialization. // if(m_h == NULL) { m_snaplen = snaplen; return; } if(is_live() && scap_set_snaplen(m_h, snaplen) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_h)); } } void sinsp::set_fullcapture_port_range(uint16_t range_start, uint16_t range_end) { // // If set_fullcapture_port_range is called before opening of the inspector, // we register the value to be set after its initialization. // if(m_h == NULL) { m_increased_snaplen_port_range = {range_start, range_end}; return; } if(!is_live()) { throw sinsp_exception("set_fullcapture_port_range called on a trace file"); } if(scap_set_fullcapture_port_range(m_h, range_start, range_end) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_h)); } } void sinsp::set_statsd_port(const uint16_t port) { // // If this method is called before opening of the inspector, // we register the value to be set after its initialization. // if(m_h == NULL) { m_statsd_port = port; return; } if(!is_live()) { throw sinsp_exception("set_statsd_port called on a trace file"); } if(scap_set_statsd_port(m_h, port) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_h)); } } void sinsp::stop_capture() { if(scap_stop_capture(m_h) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_h)); } } void sinsp::start_capture() { if(scap_start_capture(m_h) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_h)); } } void sinsp::stop_dropping_mode() { if(m_mode == SCAP_MODE_LIVE) { g_logger.format(sinsp_logger::SEV_INFO, "stopping drop mode"); if(scap_stop_dropping_mode(m_h) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_h)); } } } void sinsp::start_dropping_mode(uint32_t sampling_ratio) { if(m_mode == SCAP_MODE_LIVE) { g_logger.format(sinsp_logger::SEV_INFO, "setting drop mode to %" PRIu32, sampling_ratio); if(scap_start_dropping_mode(m_h, sampling_ratio) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_h)); } } } #ifdef HAS_FILTERING void sinsp::set_filter(sinsp_filter* filter) { if(m_filter != NULL) { ASSERT(false); throw sinsp_exception("filter can only be set once"); } m_filter = filter; } void sinsp::set_filter(const string& filter) { if(m_filter != NULL) { ASSERT(false); throw sinsp_exception("filter can only be set once"); } sinsp_filter_compiler compiler(this, filter); m_filter = compiler.compile(); m_filterstring = filter; } const string sinsp::get_filter() { return m_filterstring; } void sinsp::add_evttype_filter(string &name, set &evttypes, set &syscalls, set &tags, sinsp_filter *filter) { // Create the evttype filter if it doesn't exist. if(m_evttype_filter == NULL) { m_evttype_filter = new sinsp_evttype_filter(); } m_evttype_filter->add(name, evttypes, syscalls, tags, filter); } bool sinsp::run_filters_on_evt(sinsp_evt *evt) { // // First run the global filter, if there is one. // if(m_filter && m_filter->run(evt) == true) { return true; } // // Then run the evttype filter, if there is one. if(m_evttype_filter && m_evttype_filter->run(evt) == true) { return true; } return false; } #endif const scap_machine_info* sinsp::get_machine_info() { return m_machine_info; } const unordered_map* sinsp::get_userlist() { return &m_userlist; } scap_userinfo* sinsp::get_user(uint32_t uid) { unordered_map::const_iterator it; if(uid == 0xffffffff) { return NULL; } it = m_userlist.find(uid); if(it == m_userlist.end()) { return NULL; } return it->second; } const unordered_map* sinsp::get_grouplist() { return &m_grouplist; } #ifdef HAS_FILTERING void sinsp::get_filtercheck_fields_info(OUT vector* list) { sinsp_utils::get_filtercheck_fields_info(list); } #else void sinsp::get_filtercheck_fields_info(OUT vector* list) { } #endif uint32_t sinsp::reserve_thread_memory(uint32_t size) { if(m_h != NULL) { throw sinsp_exception("reserve_thread_memory can't be called after capture starts"); } return m_thread_privatestate_manager.reserve(size); } void sinsp::get_capture_stats(scap_stats* stats) { if(scap_get_stats(m_h, stats) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_h)); } } void sinsp::set_max_thread_table_size(uint32_t value) { uint32_t max_size = uint32_t(MAX_THREAD_TABLE_SIZE); m_max_thread_table_size = (value < max_size ? value : max_size); } #ifdef GATHER_INTERNAL_STATS sinsp_stats sinsp::get_stats() { scap_stats stats; // // Get capture stats from scap // if(m_h) { scap_get_stats(m_h, &stats); m_stats.m_n_seen_evts = stats.n_evts; m_stats.m_n_drops = stats.n_drops; m_stats.m_n_preemptions = stats.n_preemptions; } else { m_stats.m_n_seen_evts = 0; m_stats.m_n_drops = 0; m_stats.m_n_preemptions = 0; } // // Count the number of threads and fds by scanning the tables, // and update the thread-related stats. // if(m_thread_manager) { m_thread_manager->update_statistics(); } // // Return the result // return m_stats; } #endif // GATHER_INTERNAL_STATS void sinsp::set_log_callback(sinsp_logger_callback cb) { if(cb) { g_logger.add_callback_log(cb); } else { g_logger.remove_callback_log(); } } void sinsp::set_log_file(string filename) { g_logger.add_file_log(filename); } void sinsp::set_log_stderr() { g_logger.add_stderr_log(); } void sinsp::set_min_log_severity(sinsp_logger::severity sev) { g_logger.set_severity(sev); } sinsp_evttables* sinsp::get_event_info_tables() { return &g_infotables; } void sinsp::add_chisel_dir(string dirname, bool front_add) { #ifdef HAS_CHISELS 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); } #endif } void sinsp::set_buffer_format(sinsp_evt::param_fmt format) { m_buffer_format = format; } void sinsp::set_drop_event_flags(ppm_event_flags flags) { m_parser->m_drop_event_flags = flags; } sinsp_evt::param_fmt sinsp::get_buffer_format() { return m_buffer_format; } void sinsp::set_large_envs(bool enable) { m_large_envs_enabled = enable; } void sinsp::set_debug_mode(bool enable_debug) { m_isdebug_enabled = enable_debug; } void sinsp::set_print_container_data(bool print_container_data) { m_print_container_data = print_container_data; } void sinsp::set_fatfile_dump_mode(bool enable_fatfile) { m_isfatfile_enabled = enable_fatfile; } void sinsp::set_internal_events_mode(bool enable_internal_events) { m_isinternal_events_enabled = enable_internal_events; } void sinsp::set_hostname_and_port_resolution_mode(bool enable) { m_hostname_and_port_resolution_enabled = enable; } void sinsp::set_max_evt_output_len(uint32_t len) { m_max_evt_output_len = len; } sinsp_protodecoder* sinsp::require_protodecoder(string decoder_name) { return m_parser->add_protodecoder(decoder_name); } void sinsp::set_eventmask(uint32_t event_types) { if (scap_set_eventmask(m_h, event_types) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_h)); } } void sinsp::unset_eventmask(uint32_t event_id) { if (scap_unset_eventmask(m_h, event_id) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_h)); } } void sinsp::protodecoder_register_reset(sinsp_protodecoder* dec) { m_decoders_reset_list.push_back(dec); } sinsp_parser* sinsp::get_parser() { return m_parser; } bool sinsp::setup_cycle_writer(string base_file_name, int rollover_mb, int duration_seconds, int file_limit, unsigned long event_limit, bool compress) { m_compress = compress; if(rollover_mb != 0 || duration_seconds != 0 || file_limit != 0 || event_limit != 0) { m_write_cycling = true; } return m_cycle_writer->setup(base_file_name, rollover_mb, duration_seconds, file_limit, event_limit, &m_dumper); } double sinsp::get_read_progress() { if(m_input_fd != 0) { // We can't get a reliable file size, so we can't get // any reliable progress return 0; } if(m_filesize == -1) { throw sinsp_exception(scap_getlasterr(m_h)); } ASSERT(m_filesize != 0); int64_t fpos = scap_get_readfile_offset(m_h); if(fpos == -1) { throw sinsp_exception(scap_getlasterr(m_h)); } return (double)fpos * 100 / m_filesize; } bool sinsp::remove_inactive_threads() { return m_thread_manager->remove_inactive_threads(); } #ifndef CYGWING_AGENT void sinsp::init_mesos_client(string* api_server, bool verbose) { m_verbose_json = verbose; if(m_mesos_client == NULL) { if(api_server) { // -m std::string::size_type pos = api_server->find(','); if(pos != std::string::npos) { m_marathon_api_server.clear(); m_marathon_api_server.push_back(api_server->substr(pos + 1)); } m_mesos_api_server = api_server->substr(0, pos); } bool is_live = !m_mesos_api_server.empty(); m_mesos_client = new mesos(m_mesos_api_server, m_marathon_api_server, true, // mesos leader auto-follow m_marathon_api_server.empty(), // marathon leader auto-follow if no uri mesos::credentials_t(), // mesos creds, the only way to provide creds in sysdig is embedded in URI mesos::credentials_t(), // marathon creds mesos::default_timeout_ms, is_live, m_verbose_json); } } void sinsp::init_k8s_ssl(const string *ssl_cert) { #ifdef HAS_CAPTURE if(ssl_cert != nullptr && !ssl_cert->empty() && (!m_k8s_ssl || ! m_k8s_bt)) { std::string cert; std::string key; std::string key_pwd; std::string ca_cert; // -K | :[:] std::string::size_type pos = ssl_cert->find(':'); if(pos == std::string::npos) // ca_cert-only is obsoleted, single entry is now bearer token { m_k8s_bt = std::make_shared(*ssl_cert); } else { cert = ssl_cert->substr(0, pos); if(cert.empty()) { throw sinsp_exception(string("Invalid K8S SSL entry: ") + *ssl_cert); } // pos < ssl_cert->length() so it's safe to take // substr() from head, but it may be empty std::string::size_type head = pos + 1; pos = ssl_cert->find(':', head); if (pos == std::string::npos) { key = ssl_cert->substr(head); } else { key = ssl_cert->substr(head, pos - head); ca_cert = ssl_cert->substr(pos + 1); } if(key.empty()) { throw sinsp_exception(string("Invalid K8S SSL entry: ") + *ssl_cert); } // Parse the password if it exists pos = key.find('#'); if(pos != std::string::npos) { key_pwd = key.substr(pos + 1); key = key.substr(0, pos); } } g_logger.format(sinsp_logger::SEV_TRACE, "Creating sinsp_ssl with cert %s, key %s, key_pwd %s, ca_cert %s", cert.c_str(), key.c_str(), key_pwd.c_str(), ca_cert.c_str()); m_k8s_ssl = std::make_shared(cert, key, key_pwd, ca_cert, ca_cert.empty() ? false : true, "PEM"); } #endif // HAS_CAPTURE } void sinsp::make_k8s_client() { bool is_live = m_k8s_api_server && !m_k8s_api_server->empty(); m_k8s_client = new k8s(m_k8s_api_server ? *m_k8s_api_server : std::string() ,is_live // capture #ifdef HAS_CAPTURE ,m_k8s_ssl ,m_k8s_bt ,true // blocking #endif // HAS_CAPTURE ,nullptr #ifdef HAS_CAPTURE ,m_ext_list_ptr #else ,nullptr #endif // HAS_CAPTURE ); } void sinsp::init_k8s_client(string* api_server, string* ssl_cert, bool verbose) { ASSERT(api_server); m_verbose_json = verbose; m_k8s_api_server = api_server; m_k8s_api_cert = ssl_cert; #ifdef HAS_CAPTURE if(m_k8s_api_detected && m_k8s_ext_detect_done) #endif // HAS_CAPTURE { if(m_k8s_client) { delete m_k8s_client; m_k8s_client = nullptr; } init_k8s_ssl(ssl_cert); make_k8s_client(); } } void sinsp::collect_k8s() { if(m_parser) { if(m_k8s_api_server) { if(!m_k8s_client) { init_k8s_client(m_k8s_api_server, m_k8s_api_cert, m_verbose_json); if(m_k8s_client) { g_logger.log("K8s client created.", sinsp_logger::SEV_DEBUG); } else { g_logger.log("K8s client NOT created.", sinsp_logger::SEV_DEBUG); } } if(m_k8s_client) { if(m_lastevent_ts > m_k8s_last_watch_time_ns + ONE_SECOND_IN_NS) { m_k8s_last_watch_time_ns = m_lastevent_ts; g_logger.log("K8s updating state ...", sinsp_logger::SEV_DEBUG); uint64_t delta = sinsp_utils::get_current_time_ns(); m_k8s_client->watch(); m_parser->schedule_k8s_events(); delta = sinsp_utils::get_current_time_ns() - delta; g_logger.format(sinsp_logger::SEV_DEBUG, "Updating Kubernetes state took %" PRIu64 " ms", delta / 1000000LL); } } } } } void sinsp::k8s_discover_ext() { #ifdef HAS_CAPTURE try { if(m_k8s_api_server && !m_k8s_api_server->empty() && !m_k8s_ext_detect_done) { g_logger.log("K8s API extensions handler: detecting extensions.", sinsp_logger::SEV_TRACE); if(!m_k8s_ext_handler) { if(!m_k8s_collector) { m_k8s_collector = std::make_shared(); } if(uri(*m_k8s_api_server).is_secure()) { init_k8s_ssl(m_k8s_api_cert); } m_k8s_ext_handler.reset(new k8s_api_handler(m_k8s_collector, *m_k8s_api_server, "/apis/extensions/v1beta1", "[.resources[].name]", "1.1", m_k8s_ssl, m_k8s_bt, true)); g_logger.log("K8s API extensions handler: collector created.", sinsp_logger::SEV_TRACE); } else { g_logger.log("K8s API extensions handler: collecting data.", sinsp_logger::SEV_TRACE); m_k8s_ext_handler->collect_data(); if(m_k8s_ext_handler->ready()) { g_logger.log("K8s API extensions handler: data received.", sinsp_logger::SEV_TRACE); if(m_k8s_ext_handler->error()) { g_logger.log("K8s API extensions handler: data error occurred while detecting API extensions.", sinsp_logger::SEV_WARNING); m_ext_list_ptr.reset(); } else { const k8s_api_handler::api_list_t& exts = m_k8s_ext_handler->extensions(); std::ostringstream ostr; k8s_ext_list_t ext_list; for(const auto& ext : exts) { ext_list.insert(ext); ostr << std::endl << ext; } g_logger.log("K8s API extensions handler extensions found: " + ostr.str(), sinsp_logger::SEV_DEBUG); m_ext_list_ptr.reset(new k8s_ext_list_t(ext_list)); } m_k8s_ext_detect_done = true; m_k8s_collector.reset(); m_k8s_ext_handler.reset(); } else { g_logger.log("K8s API extensions handler: not ready.", sinsp_logger::SEV_TRACE); } } } } catch(std::exception& ex) { g_logger.log(std::string("K8s API extensions handler error: ").append(ex.what()), sinsp_logger::SEV_ERROR); m_k8s_ext_detect_done = false; m_k8s_collector.reset(); m_k8s_ext_handler.reset(); } g_logger.log("K8s API extensions handler: detection done.", sinsp_logger::SEV_TRACE); #endif // HAS_CAPTURE } void sinsp::update_k8s_state() { #ifdef HAS_CAPTURE try { if(m_k8s_api_server && !m_k8s_api_server->empty()) { if(!m_k8s_api_detected) { if(!m_k8s_api_handler) { if(!m_k8s_collector) { m_k8s_collector = std::make_shared(); } if(uri(*m_k8s_api_server).is_secure() && (!m_k8s_ssl || ! m_k8s_bt)) { init_k8s_ssl(m_k8s_api_cert); } m_k8s_api_handler.reset(new k8s_api_handler(m_k8s_collector, *m_k8s_api_server, "/api", ".versions", "1.1", m_k8s_ssl, m_k8s_bt, true)); } else { m_k8s_api_handler->collect_data(); if(m_k8s_api_handler->ready()) { g_logger.log("K8s API handler data received.", sinsp_logger::SEV_DEBUG); if(m_k8s_api_handler->error()) { g_logger.log("K8s API handler data error occurred while detecting API versions.", sinsp_logger::SEV_ERROR); } else { m_k8s_api_detected = m_k8s_api_handler->has("v1"); if(m_k8s_api_detected) { g_logger.log("K8s API server v1 detected.", sinsp_logger::SEV_DEBUG); } } m_k8s_collector.reset(); m_k8s_api_handler.reset(); } else { g_logger.log("K8s API handler not ready yet.", sinsp_logger::SEV_DEBUG); } } } if(m_k8s_api_detected && !m_k8s_ext_detect_done) { k8s_discover_ext(); } if(m_k8s_api_detected && m_k8s_ext_detect_done) { collect_k8s(); } } } catch(std::exception& e) { g_logger.log(std::string("Error fetching K8s data: ").append(e.what()), sinsp_logger::SEV_ERROR); throw; } #endif // HAS_CAPTURE } bool sinsp::get_mesos_data() { bool ret = false; #ifdef HAS_CAPTURE try { static time_t last_mesos_refresh = 0; ASSERT(m_mesos_client); ASSERT(m_mesos_client->is_alive()); time_t now; time(&now); if(last_mesos_refresh) { g_logger.log("Collecting Mesos data ...", sinsp_logger::SEV_DEBUG); ret = m_mesos_client->collect_data(); } if(difftime(now, last_mesos_refresh) > 10) { g_logger.log("Requesting Mesos data ...", sinsp_logger::SEV_DEBUG); m_mesos_client->send_data_request(false); last_mesos_refresh = now; } } catch(std::exception& ex) { g_logger.log(std::string("Mesos exception: ") + ex.what(), sinsp_logger::SEV_ERROR); delete m_mesos_client; m_mesos_client = NULL; init_mesos_client(0, m_verbose_json); } #endif // HAS_CAPTURE return ret; } void sinsp::update_mesos_state() { ASSERT(m_mesos_client); if(m_lastevent_ts > m_mesos_last_watch_time_ns + ONE_SECOND_IN_NS) { m_mesos_last_watch_time_ns = m_lastevent_ts; if(m_mesos_client->is_alive()) { uint64_t delta = sinsp_utils::get_current_time_ns(); if(m_parser && get_mesos_data()) { m_parser->schedule_mesos_events(); delta = sinsp_utils::get_current_time_ns() - delta; g_logger.format(sinsp_logger::SEV_DEBUG, "Updating Mesos state took %" PRIu64 " ms", delta / 1000000LL); } } else { g_logger.format(sinsp_logger::SEV_ERROR, "Mesos connection not active anymore, retrying ..."); delete m_mesos_client; m_mesos_client = NULL; init_mesos_client(0, m_verbose_json); } } } #endif // CYGWING_AGENT void sinsp::set_bpf_probe(const string& bpf_probe) { m_bpf = true; m_bpf_probe = bpf_probe; } bool sinsp::is_bpf_enabled() { // At the inspector level, bpf can be explicitly enabled via // sinsp::set_bpf_probe, but what's most important is whether // it's enabled at the libscap level, which can also be done // via the environment. if(m_h) { return scap_get_bpf_enabled(m_h); } return false; } /////////////////////////////////////////////////////////////////////////////// // Note: this is defined here so we can inline it in sinso::next /////////////////////////////////////////////////////////////////////////////// bool sinsp_thread_manager::remove_inactive_threads() { bool res = false; if(m_last_flush_time_ns == 0) { // // Set the first table scan for 30 seconds in, so that we can spot bugs in the logic without having // to wait for tens of minutes // if(m_inspector->m_inactive_thread_scan_time_ns > 30 * ONE_SECOND_IN_NS) { m_last_flush_time_ns = (m_inspector->m_lastevent_ts - m_inspector->m_inactive_thread_scan_time_ns + 30 * ONE_SECOND_IN_NS); } else { m_last_flush_time_ns = (m_inspector->m_lastevent_ts - m_inspector->m_inactive_thread_scan_time_ns); } } if(m_inspector->m_lastevent_ts > m_last_flush_time_ns + m_inspector->m_inactive_thread_scan_time_ns) { std::unordered_map to_delete; res = true; m_last_flush_time_ns = m_inspector->m_lastevent_ts; g_logger.format(sinsp_logger::SEV_INFO, "Flushing thread table"); // // Go through the table and remove dead entries. // m_threadtable.loop([&] (sinsp_threadinfo& tinfo) { bool closed = (tinfo.m_flags & PPM_CL_CLOSED) != 0; if(closed || ((m_inspector->m_lastevent_ts > tinfo.m_lastaccess_ts + m_inspector->m_thread_timeout_ns) && !scap_is_thread_alive(m_inspector->m_h, tinfo.m_pid, tinfo.m_tid, tinfo.m_comm.c_str())) ) { // // Reset the cache // m_last_tid = 0; m_last_tinfo.reset(); #ifdef GATHER_INTERNAL_STATS m_removed_threads->increment(); #endif to_delete[tinfo.m_tid] = closed; } return true; }); for (auto& it : to_delete) { remove_thread(it.first, it.second); } // // Rebalance the thread table dependency tree, so we free up threads that // exited but that are stuck because of reference counting. // recreate_child_dependencies(); } return res; } sysdig-0.26.4/userspace/libsinsp/sinsp.h000066400000000000000000001011341352731327100202330ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ /*! \mainpage libsinsp documentation \section Introduction libsinsp is a system inspection library written in C++ and implementing high level functionality like: - live capture control (start/stop/pause...) - event capture from file or the live OS - OS state reconstruction. By parsing /proc and inspecting the live event stream, libsinsp is capable of mirroring the OS process state and putting context around key OS primitives like process IDs and file descriptors. That way, these primitives can be treated like programs, files, connections and users. - parsing of OS events and conversion of events into human-readable strings - event filtering This manual includes the following sections: - \ref inspector - \ref event - \ref dump - \ref filter - \ref state */ #pragma once #include "capture_stats_source.h" #ifdef _WIN32 #pragma warning(disable: 4251 4200 4221 4190) #endif #include "tbb/concurrent_queue.h" #include "sinsp_inet.h" #include "sinsp_public.h" #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include using namespace std; #include #include "settings.h" #include "logger.h" #include "event.h" #include "filter.h" #include "dumper.h" #include "stats.h" #include "ifinfo.h" #include "container.h" #include "viewinfo.h" #include "utils.h" #ifndef VISIBILITY_PRIVATE // Some code defines VISIBILITY_PRIVATE to nothing to get private access to sinsp #define VISIBILITY_PRIVATE private: #define VISIBILITY_PROTECTED protected: #else #define VISIBILITY_PROTECTED #endif #define ONE_SECOND_IN_NS 1000000000LL #include "tuples.h" #include "fdinfo.h" #include "threadinfo.h" #include "ifinfo.h" #include "eventformatter.h" #include "sinsp_pd_callback_type.h" class sinsp_partial_transaction; class sinsp_parser; class sinsp_analyzer; class sinsp_filter; class cycle_writer; class sinsp_protodecoder; #ifndef CYGWING_AGENT class k8s; #endif class sinsp_partial_tracer; class mesos; #ifdef HAS_CAPTURE class sinsp_ssl; class sinsp_bearer_token; template class socket_data_handler; template class socket_collector; class k8s_handler; class k8s_api_handler; #endif // HAS_CAPTURE std::vector sinsp_split(const std::string &s, char delim); /*! \brief Information about a chisel */ class sinsp_chisel_details { public: std::string m_name; std::vector> m_args; }; /*! \brief Information about a group of filter/formatting fields. */ class filter_check_info { public: enum flags { FL_NONE = 0, FL_WORKS_ON_THREAD_TABLE = (1 << 0), ///< This filter check class supports filtering incomplete events that contain only valid thread info and FD info. FL_HIDDEN = (1 << 1), ///< This filter check class won't be shown by stuff like the -l sysdig command line switch. }; filter_check_info() { m_flags = 0; } string m_name; ///< Field class name. int32_t m_nfields; ///< Number of fields in this field group. const filtercheck_field_info* m_fields; ///< Array containing m_nfields field descriptions. uint32_t m_flags; }; /*! \brief sinsp library exception. */ struct sinsp_exception : std::exception { sinsp_exception() { } ~sinsp_exception() throw() { } sinsp_exception(string error_str) { m_error_str = error_str; } sinsp_exception(string error_str, int32_t scap_rc) { m_error_str = error_str; m_scap_rc = scap_rc; } char const* what() const throw() { return m_error_str.c_str(); } int32_t scap_rc() { return m_scap_rc; } string m_error_str; int32_t m_scap_rc; }; /*! \brief sinsp library exception. */ struct sinsp_capture_interrupt_exception : sinsp_exception { }; /*! \brief The default way an event is converted to string by the library */ #define DEFAULT_OUTPUT_STR "*%evt.num %evt.time %evt.cpu %proc.name (%thread.tid) %evt.dir %evt.type %evt.args" // // Internal stuff for meta event management // typedef void (*meta_event_callback)(sinsp*, void* data); class sinsp_proc_metainfo { public: sinsp_evt m_pievt; scap_evt* m_piscapevt; uint64_t* m_piscapevt_vals; uint64_t m_n_procinfo_evts; int64_t m_cur_procinfo_evt; ppm_proclist_info* m_pli; sinsp_evt* m_next_evt; }; /** @defgroup inspector Main library @{ */ /*! \brief System inspector class. This is the library entry point class. The functionality it exports includes: - live capture control (start/stop/pause...) - trace file management - event retrieval - setting capture filters */ class SINSP_PUBLIC sinsp : public capture_stats_source { public: typedef std::shared_ptr ptr; typedef std::set k8s_ext_list_t; typedef std::shared_ptr k8s_ext_list_ptr_t; sinsp(); virtual ~sinsp(); /*! \brief Start a live event capture. \param timeout_ms the optional read timeout, i.e. the time after which a call to \ref next() returns even if no events are available. @throws a sinsp_exception containing the error string is thrown in case of failure. */ virtual void open(uint32_t timeout_ms = SCAP_TIMEOUT_MS); /*! \brief Start an event capture from a trace file. \param filename the trace file name. @throws a sinsp_exception containing the error string is thrown in case of failure. */ void open(const std::string &filename); /*! \brief Start an event capture from a file descriptor. \param fd the file descriptor @throws a sinsp_exception containing the error string is thrown in case of failure. */ void fdopen(int fd); void open_nodriver(); /*! \brief Ends a capture and release all resources. */ void close(); /*! \brief Get the next event from the open capture source \param evt a \ref sinsp_evt pointer that will be initialized to point to the next available event. \return SCAP_SUCCESS if the call is successful and pevent and pcpuid contain valid data. SCAP_TIMEOUT in case the read timeout expired and no event is available. SCAP_EOF when the end of an offline capture is reached. On Failure, SCAP_FAILURE is returned and getlasterr() can be used to obtain the cause of the error. \note: the returned event can be considered valid only until the next call to \ref) */ virtual int32_t next(OUT sinsp_evt **evt); /*! \brief Get the number of events that have been captured and processed since the call to \ref open() \return the number of captured events. */ uint64_t get_num_events(); /*! \brief Set the capture snaplen, i.e. the maximum size an event parameter can reach before the driver starts truncating it. \param snaplen the snaplen for this capture instance, in bytes. \note This function can only be called for live captures. \note By default, the driver captures the first 80 bytes of the buffers coming from events like read, write, send, recv, etc. If you're not interested in payloads, smaller values will save capture buffer space and make capture files smaller. Conversely, big values should be used with care because they can easily generate huge capture files. @throws a sinsp_exception containing the error string is thrown in case of failure. */ void set_snaplen(uint32_t snaplen); /*! \brief Determine if this inspector is going to load user tables on startup. \param import_users if true, no user tables will be created for this capture. This also means that no user or group info will be written to the trace file by the -w flag. The user/group tables are necessary to use filter fields like user.name or group.name. However, creating them can increase sysdig's startup time. Moreover, they contain information that could be privacy sensitive. \note default behavior is import_users=true. @throws a sinsp_exception containing the error string is thrown in case of failure. */ void set_import_users(bool import_users); /*! \brief temporarily pauses event capture. \note This function can only be called for live captures. */ void stop_capture(); /*! \brief Restarts an event capture that had been paused with \ref stop_capture(). \note This function can only be called for live captures. */ void start_capture(); #ifdef HAS_FILTERING /*! \brief Compiles and installs the given capture filter. \param filter the filter string. Refer to the filtering language section in the sysdig website for information about the filtering syntax. @throws a sinsp_exception containing the error string is thrown in case the filter is invalid. */ void set_filter(const string& filter); /*! \brief Installs the given capture runtime filter object. \param filter the runtime filter object */ void set_filter(sinsp_filter* filter); /*! \brief Return the filter set for this capture. \return the filter previously set with \ref set_filter(), or an empty string if no filter has been set yet. */ const string get_filter(); void add_evttype_filter(std::string &name, std::set &evttypes, std::set &syscalls, std::set &tags, sinsp_filter* filter); bool run_filters_on_evt(sinsp_evt *evt); #endif /*! \brief This method can be used to specify a function to collect the library log messages. \param cb the target function that will receive the log messages. */ void set_log_callback(sinsp_logger_callback cb); /*! \brief Instruct sinsp to write its log messages to the given file. */ void set_log_file(string filename); /*! \brief Instruct sinsp to write its log messages to stderr. */ void set_log_stderr(); /*! \brief Specify the minimum severity of the messages that go into the logs emitted by the library. */ void set_min_log_severity(sinsp_logger::severity sev); /*! \brief Start writing the captured events to file. \param dump_filename the destination trace file. \param compress true to save the trace file in a compressed format. \note only the events that pass the capture filter set with \ref set_filter() will be saved to disk. \note this simplified dump interface allows only one dump per capture. For more flexibility, refer to the \ref sinsp_dumper class, that can also be combined with \ref sinsp_filter to filter what will go into the file. @throws a sinsp_exception containing the error string is thrown in case of failure. */ void autodump_start(const string& dump_filename, bool compress); /*! \brief Cycles the file pointer to a new capture file */ void autodump_next_file(); /*! \brief Stops an event dump that was started with \ref autodump_start(). @throws a sinsp_exception containing the error string is thrown in case of failure. */ void autodump_stop(); /*! \brief Populate the given vector with the full list of filter check fields that this version of the library supports. */ static void get_filtercheck_fields_info(std::vector* list); bool has_metrics(); /*! \brief Return information about the machine generating the events. \note this call works with file captures as well, because the machine info is stored in the trace files. In that case, the returned machine info is the one of the machine where the capture happened. */ const scap_machine_info* get_machine_info(); /*! \brief Look up a thread given its tid and return its information. \param tid the ID of the thread. In case of multi-thread processes, this corresponds to the PID. \return the \ref sinsp_threadinfo object containing full thread information and state. \note if you are interested in a process' information, just give this function with the PID of the process. @throws a sinsp_exception containing the error string is thrown in case of failure. */ sinsp_threadinfo* get_thread(int64_t tid); /*! \brief Look up a thread given its tid and return its information, and optionally go dig into proc if the thread is not in the thread table. \param tid the ID of the thread. In case of multi-thread processes, this corresponds to the PID. \param query_os_if_not_found if true, the library will search for this thread's information in proc, use the result to create a new thread entry, and return the new entry. \return the \ref sinsp_threadinfo object containing full thread information and state. \note if you are interested in a process' information, just give this function with the PID of the process. @throws a sinsp_exception containing the error string is thrown in case of failure. */ sinsp_threadinfo* get_thread(int64_t tid, bool query_os_if_not_found, bool lookup_only); threadinfo_map_t::ptr_t get_thread_ref(int64_t tid, bool query_os_if_not_found, bool lookup_only); /*! \brief Return the table with all the machine users. \return a hash table with the user ID (UID) as the key and the user information as the data. \note this call works with file captures as well, because the user table is stored in the trace files. In that case, the returned user list is the one of the machine where the capture happened. */ const unordered_map* get_userlist(); /*! \brief Lookup for user in the user table. \return the \ref scap_userinfo object containing full user information, if user not found, returns NULL. \note this call works with file captures as well, because the user table is stored in the trace files. In that case, the returned user list is the one of the machine where the capture happened. */ scap_userinfo* get_user(uint32_t uid); /*! \brief Return the table with all the machine user groups. \return a hash table with the group ID (GID) as the key and the group information as the data. \note this call works with file captures as well, because the group table is stored in the trace files. In that case, the returned user table is the one of the machine where the capture happened. */ const unordered_map* get_grouplist(); /*! \brief Fill the given structure with statistics about the currently open capture. \note this call won't work on file captures. */ void get_capture_stats(scap_stats* stats) override; void set_max_thread_table_size(uint32_t value); #ifdef GATHER_INTERNAL_STATS sinsp_stats get_stats(); #endif #ifdef HAS_ANALYZER sinsp_analyzer* m_analyzer; #endif /*! \brief Return the event and system call information tables. This function exports the tables containing the information about the events supported by the capture infrastructure and the available system calls. */ sinsp_evttables* get_event_info_tables(); /*! \brief get last library error. */ string getlasterr() { return m_lasterr; } /*! \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 add_chisel_dir(string dirname, bool front_add); /*! \brief Get the list of machine network interfaces. \return Pointer to the interface list manager. */ sinsp_network_interfaces* get_ifaddr_list(); /*! \brief Set the format used to render event data buffer arguments. */ void set_buffer_format(sinsp_evt::param_fmt format); /*! \brief Get the format used to render event data buffer arguments. */ sinsp_evt::param_fmt get_buffer_format(); /*! \brief Set event flags for which matching events should be dropped pre-filtering */ void set_drop_event_flags(ppm_event_flags flags); /*! \brief Returns true if the current capture is offline */ inline bool is_capture() { return m_mode == SCAP_MODE_CAPTURE; } /*! \brief Returns true if the current capture is live */ inline bool is_live() { return m_mode == SCAP_MODE_LIVE; } /*! \brief Returns true if the sysdig module is not loaded */ inline bool is_nodriver() { return m_mode == SCAP_MODE_NODRIVER; } /*! \brief Returns true if truncated environments should be loaded from /proc */ inline bool large_envs_enabled() { return is_live() && m_large_envs_enabled; } /*! \brief Enable/disable large environment support \param enable when it is true and the current capture is live environments larger than SCAP_MAX_ENV_SIZE will be loaded from /proc//environ (if possible) */ void set_large_envs(bool enable); /*! \brief Set the debugging mode of the inspector. \param enable_debug when it is true and the current capture is live the inspector filters out events about sysdig itself. */ void set_debug_mode(bool enable_debug); /*! \brief Set the fatfile mode when writing events to file. \note fatfile mode involves saving "hidden" events in the trace file that make it possible to preserve full state even when filters that would drop state packets are used during the capture. */ void set_fatfile_dump_mode(bool enable_fatfile); /*! \brief Set internal events mode. \note By default, internal events, such as events that note when new containers or orchestration entities have been created, are not returned in sinsp::next(). (They are always written to capture files, to ensure that the full state can be reconstructed when capture files are read). Enabling internal events mode will result in these events being returned. */ void set_internal_events_mode(bool enable_internal_events); /*! \brief Set whether Sysdig should resolve hostnames and port protocols or not. \note Sysdig can use the system library functions getservbyport and so to resolve protocol names and domain names. \param enable If set to false it will enable this function and use plain numerical values. */ void set_hostname_and_port_resolution_mode(bool enable); /*! \brief Set the runtime flag for resolving the timespan in a human readable mode. \note Moved to the inspector due to sysdig#426 issue \param flag Can be 'h', 'a', 'r', 'd', 'D' as documented in the manual. */ inline void set_time_output_mode(char flag) { m_output_time_flag = flag; } /*! \brief Sets the max length of event argument strings. \param len Max length after which an event argument string is truncated. 0 means no limit. Use this to reduce verbosity when printing event info on screen. */ void set_max_evt_output_len(uint32_t len); /*! \brief Returns true if the debug mode is enabled. */ inline bool is_debug_enabled() { return m_isdebug_enabled; } /*! \brief Set a flag indicating if the command line requested to show container information. \param set true if the command line argument is set to show container information */ void set_print_container_data(bool print_container_data); /*! \brief Returns true if the command line argument is set to show container information. */ inline bool is_print_container_data() { return m_print_container_data; } /*! \brief Lets a filter plugin request a protocol decoder. \param the name of the required decoder */ sinsp_protodecoder* require_protodecoder(std::string decoder_name); /*! \brief Lets a filter plugin request a protocol decoder. \param the name of the required decoder */ void protodecoder_register_reset(sinsp_protodecoder* dec); /*! \brief If this is an offline capture, return the name of the file that is being read, otherwise return an empty string. */ std::string get_input_filename() { return m_input_filename; } /*! \brief If this is an online capture, set event_id. \param event type to set \return SCAP_SUCCESS if the call is successful On Failure, SCAP_FAILURE is returned and getlasterr() can be used to obtain the cause of the error. \note For a list of event types, refer to \ref etypes. */ void set_eventmask(uint32_t event_types); /*! \brief If this is an online capture, unset event_id. \param event type to unset \return SCAP_SUCCESS if the call is successful On Failure, SCAP_FAILURE is returned and getlasterr() can be used to obtain the cause of the error. \note For a list of event types, refer to \ref etypes. */ void unset_eventmask(uint32_t event_id); /*! \brief When reading events from a trace file, this function returns the read progress as a number between 0 and 100. */ double get_read_progress(); /*! \brief Make the amount of data gathered for a syscall to be determined by the number of parameters. */ virtual int /*SCAP_X*/ dynamic_snaplen(bool enable) { if(enable) { return scap_enable_dynamic_snaplen(m_h); } else { return scap_disable_dynamic_snaplen(m_h); } } #ifndef CYGWING_AGENT void init_k8s_ssl(const std::string *ssl_cert); void init_k8s_client(std::string* api_server, std::string* ssl_cert, bool verbose = false); void make_k8s_client(); k8s* get_k8s_client() const { return m_k8s_client; } void init_mesos_client(std::string* api_server, bool verbose = false); mesos* get_mesos_client() const { return m_mesos_client; } #endif // // Misc internal stuff // void stop_dropping_mode(); void start_dropping_mode(uint32_t sampling_ratio); void on_new_entry_from_proc(void* context, scap_t* handle, int64_t tid, scap_threadinfo* tinfo, scap_fdinfo* fdinfo); void set_get_procs_cpu_from_driver(bool get_procs_cpu_from_driver) { m_get_procs_cpu_from_driver = get_procs_cpu_from_driver; } // // Used by filters to enable app event state tracking, which is disabled // by default for performance reasons // void request_tracer_state_tracking() { m_track_tracers_state = true; } // // Allocates private state in the thread info class. // Returns the ID to use when retrieving the memory area. // Will fail if called after the capture starts. // uint32_t reserve_thread_memory(uint32_t size); sinsp_parser* get_parser(); bool setup_cycle_writer(std::string base_file_name, int rollover_mb, int duration_seconds, int file_limit, unsigned long event_limit, bool compress); void import_ipv4_interface(const sinsp_ipv4_ifinfo& ifinfo); void add_meta_event(sinsp_evt *metaevt); void add_meta_event_callback(meta_event_callback cback, void* data); void remove_meta_event_callback(); void filter_proc_table_when_saving(bool filter); void enable_tracers_capture(); void enable_page_faults(); uint64_t get_bytes_read() { return scap_ftell(m_h); } void refresh_ifaddr_list(); void refresh_proc_list() { scap_refresh_proc_table(m_h); } void set_simpledriver_mode(); std::vector get_n_tracepoint_hit(); void set_bpf_probe(const std::string& bpf_probe); bool is_bpf_enabled(); static unsigned num_possible_cpus(); #ifdef CYGWING_AGENT wh_t* get_wmi_handle() { return scap_get_wmi_handle(m_h); } #endif static inline bool falco_consider_evtnum(uint16_t etype) { enum ppm_event_flags flags = g_infotables.m_event_info[etype].flags; return ! (flags & sinsp::falco_skip_flags()); } static inline bool falco_consider_syscallid(uint16_t scid) { enum ppm_event_flags flags = g_infotables.m_syscall_info_table[scid].flags; return ! (flags & sinsp::falco_skip_flags()); } // Add comm to the list of comms for which the inspector // should not return events. bool suppress_events_comm(const std::string &comm); bool check_suppressed(int64_t tid); void set_query_docker_image_info(bool query_image_info); void set_cri_extra_queries(bool extra_queries); void set_fullcapture_port_range(uint16_t range_start, uint16_t range_end); void set_statsd_port(uint16_t port); void set_cri_socket_path(const std::string& path); void set_cri_timeout(int64_t timeout_ms); VISIBILITY_PROTECTED bool add_thread(const sinsp_threadinfo *ptinfo); void set_mode(scap_mode_t value) { m_mode = value; } VISIBILITY_PRIVATE static inline ppm_event_flags falco_skip_flags() { return (ppm_event_flags) (EF_SKIPPARSERESET | EF_UNUSED | EF_DROP_FALCO); } // Doxygen doesn't understand VISIBILITY_PRIVATE #ifdef _DOXYGEN private: #endif void open_int(); void init(); void import_thread_table(); void import_ifaddr_list(); void import_user_list(); void add_protodecoders(); void remove_thread(int64_t tid, bool force); // // Note: lookup_only should be used when the query for the thread is made // not as a consequence of an event for that thread arriving, but // just for lookup reason. In that case, m_lastaccess_ts is not updated // and m_last_tinfo is not set. // inline threadinfo_map_t::ptr_t find_thread(int64_t tid, bool lookup_only) { threadinfo_map_t::ptr_t thr; // // Try looking up in our simple cache // if(tid == m_thread_manager->m_last_tid) { thr = m_thread_manager->m_last_tinfo.lock(); if (thr) { #ifdef GATHER_INTERNAL_STATS m_thread_manager->m_cached_lookups->increment(); #endif thr->m_lastaccess_ts = m_lastevent_ts; return thr; } } // // Caching failed, do a real lookup // thr = m_thread_manager->m_threadtable.get_ref(tid); if(thr) { #ifdef GATHER_INTERNAL_STATS m_thread_manager->m_non_cached_lookups->increment(); #endif if(!lookup_only) { m_thread_manager->m_last_tid = tid; m_thread_manager->m_last_tinfo = thr; thr->m_lastaccess_ts = m_lastevent_ts; } return thr; } else { #ifdef GATHER_INTERNAL_STATS m_thread_manager->m_failed_lookups->increment(); #endif return NULL; } } // this is here for testing purposes only sinsp_threadinfo* find_thread_test(int64_t tid, bool lookup_only); bool remove_inactive_threads(); #ifndef CYGWING_AGENT void k8s_discover_ext(); void collect_k8s(); void update_k8s_state(); void update_mesos_state(); bool get_mesos_data(); #endif static int64_t get_file_size(const std::string& fname, char *error); static std::string get_error_desc(const std::string& msg = ""); void restart_capture_at_filepos(uint64_t filepos); void fseek(uint64_t filepos) { scap_fseek(m_h, filepos); } void add_suppressed_comms(scap_open_args &oargs); bool increased_snaplen_port_range_set() const { return m_increased_snaplen_port_range.range_start > 0 && m_increased_snaplen_port_range.range_end > 0; } scap_t* m_h; uint32_t m_nevts; int64_t m_filesize; scap_mode_t m_mode = SCAP_MODE_NONE; // If non-zero, reading from this fd and m_input_filename contains "fd // ". Otherwise, reading from m_input_filename. int m_input_fd; std::string m_input_filename; bool m_bpf; std::string m_bpf_probe; bool m_isdebug_enabled; bool m_isfatfile_enabled; bool m_isinternal_events_enabled; bool m_hostname_and_port_resolution_enabled; char m_output_time_flag; uint32_t m_max_evt_output_len; bool m_compress; sinsp_evt m_evt; std::string m_lasterr; int64_t m_tid_to_remove; int64_t m_tid_of_fd_to_remove; std::vector* m_fds_to_remove; uint64_t m_lastevent_ts; // the parsing engine sinsp_parser* m_parser; // the statistics analysis engine scap_dumper_t* m_dumper; bool m_is_dumping; bool m_filter_proc_table_when_saving; const scap_machine_info* m_machine_info; uint32_t m_num_cpus; sinsp_thread_privatestate_manager m_thread_privatestate_manager; bool m_is_tracers_capture_enabled; // This is used to support reading merged files, where the capture needs to // restart in the middle of the file. uint64_t m_file_start_offset; bool m_flush_memory_dump; bool m_large_envs_enabled; sinsp_network_interfaces* m_network_interfaces; public: sinsp_thread_manager* m_thread_manager; sinsp_container_manager m_container_manager; // // Kubernetes // #ifndef CYGWING_AGENT std::string* m_k8s_api_server; std::string* m_k8s_api_cert; #ifdef HAS_CAPTURE std::shared_ptr m_k8s_ssl; std::shared_ptr m_k8s_bt; unique_ptr m_k8s_api_handler; shared_ptr>> m_k8s_collector; bool m_k8s_api_detected = false; unique_ptr m_k8s_ext_handler; k8s_ext_list_ptr_t m_ext_list_ptr; bool m_k8s_ext_detect_done = false; #endif // HAS_CAPTURE k8s* m_k8s_client; uint64_t m_k8s_last_watch_time_ns; #endif // CYGWING_AGENT // // Mesos/Marathon // std::string m_mesos_api_server; std::vector m_marathon_api_server; mesos* m_mesos_client; uint64_t m_mesos_last_watch_time_ns; // // True if sysdig is ran with -v. // Used by mesos and k8s objects. // bool m_verbose_json = false; // // True if the command line argument is set to show container information // The default is false set within the constructor // bool m_print_container_data; #ifdef HAS_FILTERING uint64_t m_firstevent_ts; sinsp_filter* m_filter; sinsp_evttype_filter *m_evttype_filter; std::string m_filterstring; #endif // // Internal stats // #ifdef GATHER_INTERNAL_STATS sinsp_stats m_stats; #endif int32_t m_n_proc_lookups; uint64_t m_n_proc_lookups_duration_ns; int32_t m_max_n_proc_lookups = -1; int32_t m_max_n_proc_socket_lookups = -1; #ifdef HAS_ANALYZER std::vector m_tid_collisions; #endif // // Saved snaplen // uint32_t m_snaplen; // // Saved increased capture range // struct { uint16_t range_start; uint16_t range_end; } m_increased_snaplen_port_range; int32_t m_statsd_port; // // Some thread table limits // uint32_t m_max_thread_table_size; uint32_t m_max_fdtable_size; uint64_t m_thread_timeout_ns; uint64_t m_inactive_thread_scan_time_ns; // // Container limits // uint64_t m_inactive_container_scan_time_ns; // // How to render the data buffers // sinsp_evt::param_fmt m_buffer_format; // // User and group tables // bool m_import_users; unordered_map m_userlist; unordered_map m_grouplist; // // The cycle-writer for files // cycle_writer* m_cycle_writer; bool m_write_cycling; #ifdef SIMULATE_DROP_MODE // // Some dropping infrastructure // bool m_isdropping; #endif // // App events // bool m_track_tracers_state; list m_partial_tracers_list; simple_lifo_queue* m_partial_tracers_pool; // // Protocol decoding state // std::vector m_decoders_reset_list; // // meta event management for other sources like k8s, mesos. // sinsp_evt* m_metaevt; meta_event_callback m_meta_event_callback; void* m_meta_event_callback_data; // A queue of pending container events. Written from async // callbacks that occur after looking up container // information, read from sinsp::next(). tbb::concurrent_queue> m_pending_container_evts; // Holds an event dequeued from the above queue std::shared_ptr m_container_evt; // // End of second housekeeping // bool m_get_procs_cpu_from_driver; uint64_t m_next_flush_time_ns; uint64_t m_last_procrequest_tod; sinsp_proc_metainfo m_meinfo; uint64_t m_next_stats_print_time_ns; static unsigned int m_num_possible_cpus; #if defined(HAS_CAPTURE) int64_t m_sysdig_pid; #endif // Any thread with a comm in this set will not have its events // returned in sinsp::next() std::set m_suppressed_comms; friend class sinsp_parser; friend class sinsp_analyzer; friend class sinsp_analyzer_parsers; friend class sinsp_evt; friend class sinsp_threadinfo; friend class sinsp_fdtable; friend class sinsp_thread_manager; friend class sinsp_container_manager; friend class sinsp_dumper; friend class sinsp_analyzer_fd_listener; friend class sinsp_chisel; friend class sinsp_tracerparser; friend class sinsp_filter_check_event; friend class sinsp_protodecoder; friend class lua_cbacks; friend class sinsp_filter_check_container; friend class sinsp_worker; friend class sinsp_table; friend class curses_textbox; friend class sinsp_filter_check_fd; friend class sinsp_filter_check_k8s; friend class sinsp_filter_check_mesos; friend class sinsp_filter_check_evtin; friend class sinsp_baseliner; friend class sinsp_memory_dumper; friend class sinsp_network_interfaces; friend class test_helper; template friend class sinsp_connection_manager; #ifdef SYSDIG_TEST protected: void inject_machine_info(const scap_machine_info *value) { m_machine_info = value; } void inject_network_interfaces(sinsp_network_interfaces *value) { m_network_interfaces = value; } #endif // SYSDIG_TEST }; /*@}*/ sysdig-0.26.4/userspace/libsinsp/sinsp.vcxproj000066400000000000000000000163251352731327100215060ustar00rootroot00000000000000 Debug Win32 Release Win32 {A45C552E-F4BA-480A-A7D3-EB1E9DDFBE6C} Win32Proj DynamicLibrary true v110 DynamicLibrary false v110 true $(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath);.;../../driver $(ProjectDir)\..\Debug\ $(ProjectDir)\..\Debug\ true $(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath);.;../../driver $(ProjectDir)\..\Release\ $(ProjectDir)\..\Release\ WIN32;_CRT_SECURE_NO_WARNINGS;PLATFORM_NAME="Windows";_DEBUG;_WINDOWS;_USRDLL;SINSP_EXPORTS;_NO_DEBUG_HEAP;%(PreprocessorDefinitions) MultiThreadedDebugDLL Level3 ProgramDatabase Disabled third-party/jsoncpp;../../common;../libscap;../../../draios_win32_deps/LuaJIT/src MachineX86 true Console $(ProjectDir)\..\Debug\$(TargetName).lib ../../../draios_win32_deps/LuaJIT/src/lua51.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Ws2_32.lib;%(AdditionalDependencies);../Debug/scap.lib $(ProjectDir)\..\Debug\$(TargetName)$(TargetExt) WIN32;_CRT_SECURE_NO_WARNINGS;PLATFORM_NAME="Windows";NDEBUG;_WINDOWS;_USRDLL;SINSP_EXPORTS;%(PreprocessorDefinitions) MultiThreadedDLL Level3 ProgramDatabase third-party/jsoncpp;../../common;../libscap;../../../draios_win32_deps/LuaJIT/src MachineX86 true Console true true $(ProjectDir)\..\Release\$(TargetName).lib ../../../draios_win32_deps/LuaJIT/src/lua51.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Ws2_32.lib;../Release/scap.lib;%(AdditionalDependencies) $(ProjectDir)\..\Release\$(TargetName)$(TargetExt) sysdig-0.26.4/userspace/libsinsp/sinsp.vcxproj.filters000066400000000000000000000100121352731327100231400ustar00rootroot00000000000000 Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {0c4ca121-756a-4ac8-84db-8e9eb7d79d8a} {7a30d38b-3fdd-4205-82aa-01206446965d} sysdig-0.26.4/userspace/libsinsp/sinsp_auth.cpp000066400000000000000000000075351352731327100216210ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // sinsp_auth.cpp // // Authentication/verification utilities // #if defined(__linux__) #include "sinsp_auth.h" #include #include #include #include #include #include // // sinsp_ssl // sinsp_ssl::sinsp_ssl(const std::string& cert, const std::string& key, const std::string& key_passphrase, const std::string& ca_cert, bool verify_peer, const std::string& cert_type): m_cert_type(cert_type), m_cert(cert), m_key(key), m_key_passphrase(key_passphrase), m_ca_cert(ca_cert), m_verify_peer(verify_peer) { } sinsp_ssl::~sinsp_ssl() { } std::string sinsp_ssl::memorize_file(const std::string& disk_file) { std::string mem_file; if(disk_file.empty()) { return mem_file; } std::string::size_type pos = disk_file.rfind('/'); if(pos == std::string::npos) { mem_file.append(1, '/').append(disk_file); } else { mem_file.append(disk_file.substr(pos, disk_file.size() - pos)); } mem_file.append(1, '~'); int fd = shm_open(mem_file.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); if(fd != -1) { char buf[FILENAME_MAX] = { 0 }; std::ifstream ifs(disk_file); std::string fd_path = "/proc/self/fd/" + std::to_string(fd); ssize_t sz = readlink(fd_path.c_str(), buf, sizeof(buf)); if(sz != -1 && sz <= static_cast(sizeof(buf))) { mem_file.assign(buf, sz); std::string str; std::ofstream ofs(mem_file, std::ofstream::out); while(std::getline(ifs, str)) { ofs << str << '\n'; } } else { std::ostringstream os; os << "Error occurred while trying to determine the real path of memory file [" << fd_path << "]: " << strerror(errno) << " (disk file [" << disk_file << "] will be used)."; g_logger.log(os.str(), sinsp_logger::SEV_WARNING); return disk_file; } } else { std::ostringstream os; os << "Memory file creation error: " << strerror(errno) << " (disk file [" << disk_file << "] will be used)."; g_logger.log(os.str(), sinsp_logger::SEV_WARNING); return disk_file; } return mem_file; } void sinsp_ssl::unmemorize_file(const std::string& mem_file) { if(shm_unlink(mem_file.c_str()) == 0) { std::ostringstream os; os << "Memory file [" << mem_file << "] unlink error: " << strerror(errno); g_logger.log(os.str(), sinsp_logger::SEV_WARNING); } } // // bearer_token // sinsp_bearer_token::sinsp_bearer_token(const std::string& bearer_token_file, bool curl_support): m_bearer_token(stringize_file(bearer_token_file)), m_bt_auth_header(nullptr) { if(curl_support) { std::size_t len = m_bearer_token.length(); // curl does not tolerate newlines in headers while(len && (m_bearer_token[len-1] == '\r' || m_bearer_token[len-1] == '\n')) { m_bearer_token.erase(len-1); len = m_bearer_token.length(); } if(len) { std::string hdr = "Authorization: Bearer "; hdr.append(m_bearer_token); m_bt_auth_header = curl_slist_append(m_bt_auth_header, hdr.c_str()); } } } sinsp_bearer_token::~sinsp_bearer_token() { if(m_bt_auth_header) { curl_slist_free_all(m_bt_auth_header); } } std::string sinsp_bearer_token::stringize_file(const std::string& disk_file) { std::string tmp, content; std::ifstream ifs(disk_file); while(std::getline(ifs, tmp)) { content.append(tmp).append(1, '\n'); } return content; } #endif // __linux__ sysdig-0.26.4/userspace/libsinsp/sinsp_auth.h000066400000000000000000000053071352731327100212610ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // sinsp_auth.h // // Authentication/verification utilities // #if defined(__linux__) #pragma once #include "sinsp.h" #include "sinsp_int.h" #include "uri.h" #include "curl/curl.h" #include #include class sinsp_ssl { public: typedef std::shared_ptr ptr_t; sinsp_ssl(const std::string& cert, const std::string& key, const std::string& key_passphrase = "", const std::string& ca_cert = "", bool verify_peer = false, const std::string& cert_type = "PEM"); ~sinsp_ssl(); const std::string& cert_type() const; const std::string& cert() const; const std::string& key() const; const std::string& key_passphrase() const; const std::string& ca_cert() const; bool verify_peer() const; private: static std::string memorize_file(const std::string& disk_file); static void unmemorize_file(const std::string& mem_file); std::string m_cert_type; std::string m_cert; std::string m_key; std::string m_key_passphrase; std::string m_ca_cert; bool m_verify_peer = false; }; class sinsp_bearer_token { public: typedef std::shared_ptr ptr_t; sinsp_bearer_token(const std::string& bearer_token_file, bool curl_support = true); ~sinsp_bearer_token(); const std::string& get_token() const; struct curl_slist* bt_auth_header(); private: static std::string stringize_file(const std::string& disk_file); std::string m_bearer_token; struct curl_slist* m_bt_auth_header; }; // // ssl // inline const std::string& sinsp_ssl::cert_type() const { return m_cert_type; } inline const std::string& sinsp_ssl::cert() const { return m_cert; } inline const std::string& sinsp_ssl::key() const { return m_key; } inline const std::string& sinsp_ssl::key_passphrase() const { return m_key_passphrase; } inline const std::string& sinsp_ssl::ca_cert() const { return m_ca_cert; } inline bool sinsp_ssl::verify_peer() const { return m_verify_peer; } // // bearer_token // inline const std::string& sinsp_bearer_token::get_token() const { return m_bearer_token; } inline struct curl_slist* sinsp_bearer_token::bt_auth_header() { return m_bt_auth_header; } #endif // __linux__ sysdig-0.26.4/userspace/libsinsp/sinsp_curl.cpp000066400000000000000000000306761352731327100216270ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // sinsp_curl.cpp // // Curl utility // #if defined(__linux__) #include "sinsp_curl.h" #include "http_reason.h" #include #include #include #include #include #include #include sinsp_curl_http_headers::sinsp_curl_http_headers(): m_curl_header_list(NULL) { } sinsp_curl_http_headers::~sinsp_curl_http_headers() { if(m_curl_header_list) { curl_slist_free_all(m_curl_header_list); } } void sinsp_curl_http_headers::add(const string& header) { m_curl_header_list = curl_slist_append(m_curl_header_list, header.c_str()); } sinsp_curl::data sinsp_curl::m_config; sinsp_curl::sinsp_curl(const uri& url, long timeout_ms, bool debug): m_curl(curl_easy_init()), m_uri(url), m_timeout_ms(timeout_ms), m_debug(debug) { init(); } sinsp_curl::sinsp_curl(const uri& url, const std::string& bearer_token_file, long timeout_ms, bool debug): m_curl(curl_easy_init()), m_uri(url), m_timeout_ms(timeout_ms), m_bt(new bearer_token(bearer_token_file)), m_debug(debug) { init(); } sinsp_curl::sinsp_curl(const uri& url, const std::string& cert, const std::string& key, const std::string& key_passphrase, const std::string& ca_cert, bool verify_peer, const std::string& cert_type, const std::string& bearer_token_file, long timeout_ms, bool debug): m_curl(curl_easy_init()), m_uri(url), m_timeout_ms(timeout_ms), m_ssl(new ssl(cert, key, key_passphrase, ca_cert, verify_peer, cert_type)), m_bt(new bearer_token(bearer_token_file)), m_debug(debug) { init(); } sinsp_curl::sinsp_curl(const uri& url, ssl::ptr_t p_ssl, bearer_token::ptr_t p_bt, long timeout_ms, bool debug): m_curl(curl_easy_init()), m_uri(url), m_timeout_ms(timeout_ms), m_ssl(p_ssl), m_bt(p_bt), m_debug(debug) { init(); } void sinsp_curl::init() { if(!m_curl) { throw sinsp_exception("Cannot initialize CURL."); } check_error(curl_easy_setopt(m_curl, CURLOPT_FORBID_REUSE, 1L)); if(m_ssl) { init_ssl(m_curl, m_ssl); } if(m_bt) { init_bt(m_curl, m_bt); } enable_debug(m_curl, m_debug); m_response_code = -1; } sinsp_curl::~sinsp_curl() { curl_easy_cleanup(m_curl); } void sinsp_curl::init_bt(CURL* curl, bearer_token::ptr_t bt) { if(bt && bt->bt_auth_header()) { check_error(curl_easy_setopt(curl, CURLOPT_HTTPHEADER, bt->bt_auth_header())); } } void sinsp_curl::enable_debug(CURL* curl, bool enable) { if(curl) { curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, &sinsp_curl::trace); curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &m_config); long en = 0L; if(enable) { en = 1L; } m_config.trace_ascii = en; curl_easy_setopt(curl, CURLOPT_VERBOSE, en); } } void sinsp_curl::init_ssl(CURL* curl, ssl::ptr_t ssl_data) { if(curl && ssl_data) { if(!ssl_data->cert().empty()) { if(!ssl_data->cert_type().empty()) { check_error(curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, ssl_data->cert_type().c_str())); } check_error(curl_easy_setopt(curl, CURLOPT_SSLCERT, ssl_data->cert().c_str())); g_logger.log("CURL SSL certificate: " + ssl_data->cert(), sinsp_logger::SEV_DEBUG); } if(!ssl_data->key_passphrase().empty()) { check_error(curl_easy_setopt(curl, CURLOPT_KEYPASSWD, ssl_data->key_passphrase().c_str())); g_logger.log("CURL SSL key password SET. ", sinsp_logger::SEV_DEBUG); } if(!ssl_data->key().empty()) { if(!ssl_data->cert_type().empty()) { check_error(curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, ssl_data->cert_type().c_str())); } check_error(curl_easy_setopt(curl, CURLOPT_SSLKEY, ssl_data->key().c_str())); g_logger.log("CURL SSL key: " + ssl_data->key(), sinsp_logger::SEV_DEBUG); } if(ssl_data->verify_peer()) { check_error(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L)); check_error(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L)); g_logger.log("CURL SSL peer and host verification ENABLED.", sinsp_logger::SEV_DEBUG); } else { check_error(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0)); check_error(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0)); g_logger.log("CURL SSL peer and host verification DISABLED.", sinsp_logger::SEV_DEBUG); } if(!ssl_data->ca_cert().empty()) { check_error(curl_easy_setopt(curl, CURLOPT_CAINFO, ssl_data->ca_cert().c_str())); g_logger.log("CURL SSL CA cert set to: " + ssl_data->ca_cert(), sinsp_logger::SEV_DEBUG); } } } string sinsp_curl::get_data(bool do_log) { std::ostringstream os; if(get_data(os)) { return os.str(); } if(do_log) { g_logger.log("CURL error while connecting to " + m_uri.to_string(false) + ", " "response: [" + os.str() + ']', sinsp_logger::SEV_ERROR); } return ""; } size_t sinsp_curl::header_callback(char *buffer, size_t size, size_t nitems, void *userdata) { size_t sz = nitems * size; std::string buf(buffer, sz); const std::string loc = "Location:"; const std::string nl = "\r\n"; std::string::size_type loc_pos = buf.find(loc); std::string::size_type nl_pos = buf.find(nl); if((loc_pos != std::string::npos) && (nl_pos != std::string::npos) && (nl_pos - loc.length() > (loc + nl).length())) { std::string::size_type url_pos = buf.find("http://", loc_pos); if(url_pos == std::string::npos) { url_pos = buf.find("//", loc_pos); if(url_pos != std::string::npos) // still absolute { buf = buf.substr(url_pos, nl_pos-url_pos); buf.insert(0, "http:"); } else // relative { buf = buf.substr(loc.length(), nl_pos-loc.length()); } } else { buf = buf.substr(url_pos, nl_pos-url_pos); } trim(buf); sz = buf.length(); if(sz < CURL_MAX_HTTP_HEADER) { g_logger.log("HTTP redirect Location: (" + buf + ')', sinsp_logger::SEV_TRACE); std::strncpy((char*) userdata, buf.data(), sz); ((char*) userdata)[sz] = 0; } } return nitems * size; } bool sinsp_curl::is_redirect(long http_code) { return ((http_code >= 301 && http_code <= 303) || (http_code >= 307 && http_code <= 308)); } bool sinsp_curl::handle_redirect(uri& url, std::string&& loc, std::ostream& os) { if(!loc.empty()) { g_logger.log("HTTP redirect received from [" + url.to_string(false) + ']', sinsp_logger::SEV_INFO); std::string::size_type url_pos = loc.find("//"); if(url_pos != std::string::npos) { uri::credentials_t creds; url.get_credentials(creds); url = trim(loc); if(!creds.first.empty()) { url.set_credentials(creds); } } else // location relative, take just path { url.set_path(trim(loc)); } g_logger.log("HTTP redirecting to [" + url.to_string(false) + "].", sinsp_logger::SEV_INFO); return true; } else { g_logger.log("CURL redirect received from [" + url.to_string(false) + "], " "but location not found.", sinsp_logger::SEV_ERROR); return false; } return false; } size_t read_data(void* buffer, size_t size, size_t nmemb, void* instream) { auto body = (stringstream*) instream; body->read((char*) buffer, size*nmemb); return body->gcount(); } bool sinsp_curl::get_data(std::ostream& os) { CURLcode res = CURLE_OK; check_error(curl_easy_setopt(m_curl, CURLOPT_URL, m_uri.to_string().c_str())); check_error(curl_easy_setopt(m_curl, CURLOPT_HEADERDATA, m_redirect)); check_error(curl_easy_setopt(m_curl, CURLOPT_HEADERFUNCTION, header_callback)); check_error(curl_easy_setopt(m_curl, CURLOPT_CONNECTTIMEOUT, static_cast(m_timeout_ms / 1000))); check_error(curl_easy_setopt(m_curl, CURLOPT_TIMEOUT_MS, m_timeout_ms)); check_error(curl_easy_setopt(m_curl, CURLOPT_NOSIGNAL, 1)); //Prevent "longjmp causes uninitialized stack frame" bug check_error(curl_easy_setopt(m_curl, CURLOPT_ACCEPT_ENCODING, "deflate")); check_error(curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, &sinsp_curl::write_data)); check_error(curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &os)); check_error(curl_easy_setopt(m_curl, CURLOPT_READFUNCTION, &read_data)); check_error(curl_easy_setopt(m_curl, CURLOPT_READDATA, &m_body)); if(m_headers.ptr() != NULL) { setopt(CURLOPT_HTTPHEADER, m_headers.ptr()); } res = curl_easy_perform(m_curl); if(res != CURLE_OK) { os << curl_easy_strerror(res) << std::flush; } else { // HTTP errors are not returned by curl API // error will be in the response stream check_error(curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &m_response_code)); if(m_response_code >= 400) { g_logger.log("CURL HTTP error while accessing [" + m_uri.to_string(false) + "]: " + std::to_string(m_response_code) + " (" + http_reason::get(m_response_code) + ')', sinsp_logger::SEV_ERROR); return false; } else if(is_redirect(m_response_code)) { g_logger.log("HTTP redirect (" + std::to_string(m_response_code) + ')', sinsp_logger::SEV_DEBUG); if(handle_redirect(m_uri, std::string(m_redirect), os)) { std::ostringstream* pos = dynamic_cast(&os); if(pos) { pos->str(""); return get_data(*pos); } else { g_logger.log("HTTP redirect received from [" + m_uri.to_string(false) + "] but " "output stream can not be obtained (dynamic cast failed).", sinsp_logger::SEV_ERROR); return false; } } } } return res == CURLE_OK; } size_t sinsp_curl::write_data(void *ptr, size_t size, size_t nmemb, void *cb) { std::string data(reinterpret_cast(ptr), static_cast(size * nmemb)); *reinterpret_cast(cb) << data << std::flush; return size * nmemb; } bool sinsp_curl::check_error(unsigned ret, bool exc) { if(ret >= CURL_LAST && exc) { throw sinsp_exception("Invalid CURL return value:" + std::to_string(ret)); } else { return false; } CURLcode res = (CURLcode)ret; if(CURLE_OK != res && CURLE_AGAIN != res && exc) { std::ostringstream os; os << "Error: " << curl_easy_strerror(res); throw sinsp_exception(os.str()); } else { return false; } return true; } void sinsp_curl::dump(const char *text, unsigned char *ptr, size_t size, char nohex) { const std::size_t DBG_BUF_SIZE = 1024; char stream[DBG_BUF_SIZE] = { 0 }; std::ostringstream os; size_t i; size_t c; unsigned int width = 0x10; if(nohex) { width = 0x40; } snprintf(stream, DBG_BUF_SIZE, "%s, %10.10ld bytes (0x%8.8lx)\n", text, (long)size, (long)size); os << stream; for(i=0; i=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.'); os << stream; if(nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) { i+=(c+3-width); break; } } snprintf(stream, DBG_BUF_SIZE, "%c", '\n'); os << stream; } g_logger.log("CURL: " + os .str(), sinsp_logger::SEV_DEBUG); } int sinsp_curl::trace(CURL *handle, curl_infotype type, char *data, size_t size, void *userp) { struct data *config = (struct data *)userp; const char *text; (void)handle; // prevent compiler warning switch (type) { case CURLINFO_TEXT: fprintf(stderr, "== Info: %s", data); default: // in case a new one is introduced to shock us return 0; case CURLINFO_HEADER_OUT: text = "=> Send header"; break; case CURLINFO_DATA_OUT: text = "=> Send data"; break; case CURLINFO_SSL_DATA_OUT: text = "=> Send SSL data"; break; case CURLINFO_HEADER_IN: text = "<= Recv header"; break; case CURLINFO_DATA_IN: text = "<= Recv data"; break; case CURLINFO_SSL_DATA_IN: text = "<= Recv SSL data"; break; } dump(text, (unsigned char *)data, size, config->trace_ascii); return 0; } void sinsp_curl::set_body(const string& data) { m_body.clear(); m_body << data; add_header(string("Content-Length: ") + to_string(data.size())); } #endif // __linux__ sysdig-0.26.4/userspace/libsinsp/sinsp_curl.h000066400000000000000000000105421352731327100212620ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // sinsp_curl.h // // Curl utility // #if defined(__linux__) #pragma once #include "sinsp.h" #include "sinsp_int.h" #include "sinsp_auth.h" #include "uri.h" #include "curl/curl.h" #include #include class sinsp_curl_http_headers { public: sinsp_curl_http_headers(); ~sinsp_curl_http_headers(); void add(const string& header); struct curl_slist* ptr() { return m_curl_header_list; } private: struct curl_slist* m_curl_header_list; }; class sinsp_curl { public: typedef sinsp_ssl ssl; typedef sinsp_bearer_token bearer_token; static const long DEFAULT_TIMEOUT_MS = 5000L; sinsp_curl(const uri& url, long timeout_ms = DEFAULT_TIMEOUT_MS, bool debug = false); sinsp_curl(const uri& url, const std::string& bearer_token_file, long timeout_ms = DEFAULT_TIMEOUT_MS, bool debug = false); sinsp_curl(const uri& url, const std::string& cert, const std::string& key, const std::string& key_passphrase = "", const std::string& ca_cert = "", bool verify_peer = false, const std::string& cert_type = "PEM", const std::string& bearer_token_file = "", long timeout_ms = DEFAULT_TIMEOUT_MS, bool debug = false); sinsp_curl(const uri& url, ssl::ptr_t p_ssl = 0, bearer_token::ptr_t p_bt = 0, long timeout_ms = DEFAULT_TIMEOUT_MS, bool debug = false); ~sinsp_curl(); bool get_data(std::ostream& os); std::string get_data(bool do_log = true); void set_timeout(long seconds); long get_timeout() const; void set_url(const std::string& url); std::string get_url(bool show_creds = true) const; void set_body(const string& data); bool is_secure() const; ssl::ptr_t get_ssl(); template void setopt(Opt opt, Arg arg) { check_error(curl_easy_setopt(m_curl, opt, arg)); } void enable_debug() { sinsp_curl::enable_debug(m_curl); } template void add_header(T body) { m_headers.add(forward(body)); } static void init_ssl(CURL* curl, ssl::ptr_t ssl_data); bearer_token::ptr_t get_bt(); static void init_bt(CURL* curl, bearer_token::ptr_t bt); static void enable_debug(CURL* curl, bool enable = true); static bool check_error(unsigned ret, bool exc = true); static size_t header_callback(char *buffer, size_t size, size_t nitems, void *userdata); static bool is_redirect(long http_code); static bool handle_redirect(uri& url, std::string&& loc, std::ostream& os); static size_t write_data(void *ptr, size_t size, size_t nmemb, void *cb); const vector& response_headers() { return m_response_headers; } long get_response_code() const { return m_response_code; } private: struct data { char trace_ascii; // 1 or 0 }; static data m_config; static void dump(const char *text, unsigned char *ptr, size_t size, char nohex); static int trace(CURL *handle, curl_infotype type, char *data, size_t size, void *userp); void init(); CURL* m_curl; uri m_uri; long m_timeout_ms; ssl::ptr_t m_ssl; bearer_token::ptr_t m_bt; bool m_debug; char m_redirect[CURL_MAX_HTTP_HEADER] = {0}; stringstream m_body; sinsp_curl_http_headers m_headers; vector m_response_headers; long m_response_code; }; inline void sinsp_curl::set_timeout(long milliseconds) { m_timeout_ms = milliseconds; } inline long sinsp_curl::get_timeout() const { return m_timeout_ms; } inline void sinsp_curl::set_url(const std::string& url) { m_uri = url; } inline std::string sinsp_curl::get_url(bool show_creds) const { return m_uri.to_string(show_creds); } inline bool sinsp_curl::is_secure() const { return m_uri.is_secure(); } inline sinsp_curl::ssl::ptr_t sinsp_curl::get_ssl() { return m_ssl; } inline sinsp_curl::bearer_token::ptr_t sinsp_curl::get_bt() { return m_bt; } #endif // __linux__ sysdig-0.26.4/userspace/libsinsp/sinsp_errno.h000066400000000000000000000226551352731327100214520ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #define SE_EPERM 1 /* Operation not permitted */ #define SE_ENOENT 2 /* No such file or directory */ #define SE_ESRCH 3 /* No such process */ #define SE_EINTR 4 /* Interrupted system call */ #define SE_EIO 5 /* I/O error */ #define SE_ENXIO 6 /* No such device or address */ #define SE_E2BIG 7 /* Arg list too long */ #define SE_ENOEXEC 8 /* Exec format error */ #define SE_EBADF 9 /* Bad file number */ #define SE_ECHILD 10 /* No child processes */ #define SE_EAGAIN 11 /* Try again */ #define SE_ENOMEM 12 /* Out of memory */ #define SE_EACCES 13 /* Permission denied */ #define SE_EFAULT 14 /* Bad address */ #define SE_ENOTBLK 15 /* Block device required */ #define SE_EBUSY 16 /* Device or resource busy */ #define SE_EEXIST 17 /* File exists */ #define SE_EXDEV 18 /* Cross-device link */ #define SE_ENODEV 19 /* No such device */ #define SE_ENOTDIR 20 /* Not a directory */ #define SE_EISDIR 21 /* Is a directory */ #define SE_EINVAL 22 /* Invalid argument */ #define SE_ENFILE 23 /* File table overflow */ #define SE_EMFILE 24 /* Too many open files */ #define SE_ENOTTY 25 /* Not a typewriter */ #define SE_ETXTBSY 26 /* Text file busy */ #define SE_EFBIG 27 /* File too large */ #define SE_ENOSPC 28 /* No space left on device */ #define SE_ESPIPE 29 /* Illegal seek */ #define SE_EROFS 30 /* Read-only file system */ #define SE_EMLINK 31 /* Too many links */ #define SE_EPIPE 32 /* Broken pipe */ #define SE_EDOM 33 /* Math argument out of domain of func */ #define SE_ERANGE 34 /* Math result not representable */ #define SE_EDEADLK 35 /* Resource deadlock would occur */ #define SE_ENAMETOOLONG 36 /* File name too long */ #define SE_ENOLCK 37 /* No record locks available */ #define SE_ENOSYS 38 /* Function not implemented */ #define SE_ENOTEMPTY 39 /* Directory not empty */ #define SE_ELOOP 40 /* Too many symbolic links encountered */ #define SE_EWOULDBLOCK EAGAIN /* Operation would block */ #define SE_ENOMSG 42 /* No message of desired type */ #define SE_EIDRM 43 /* Identifier removed */ #define SE_ECHRNG 44 /* Channel number out of range */ #define SE_EL2NSYNC 45 /* Level 2 not synchronized */ #define SE_EL3HLT 46 /* Level 3 halted */ #define SE_EL3RST 47 /* Level 3 reset */ #define SE_ELNRNG 48 /* Link number out of range */ #define SE_EUNATCH 49 /* Protocol driver not attached */ #define SE_ENOCSI 50 /* No CSI structure available */ #define SE_EL2HLT 51 /* Level 2 halted */ #define SE_EBADE 52 /* Invalid exchange */ #define SE_EBADR 53 /* Invalid request descriptor */ #define SE_EXFULL 54 /* Exchange full */ #define SE_ENOANO 55 /* No anode */ #define SE_EBADRQC 56 /* Invalid request code */ #define SE_EBADSLT 57 /* Invalid slot */ #define SE_EDEADLOCK EDEADLK #define SE_EBFONT 59 /* Bad font file format */ #define SE_ENOSTR 60 /* Device not a stream */ #define SE_ENODATA 61 /* No data available */ #define SE_ETIME 62 /* Timer expired */ #define SE_ENOSR 63 /* Out of streams resources */ #define SE_ENONET 64 /* Machine is not on the network */ #define SE_ENOPKG 65 /* Package not installed */ #define SE_EREMOTE 66 /* Object is remote */ #define SE_ENOLINK 67 /* Link has been severed */ #define SE_EADV 68 /* Advertise error */ #define SE_ESRMNT 69 /* Srmount error */ #define SE_ECOMM 70 /* Communication error on send */ #define SE_EPROTO 71 /* Protocol error */ #define SE_EMULTIHOP 72 /* Multihop attempted */ #define SE_EDOTDOT 73 /* RFS specific error */ #define SE_EBADMSG 74 /* Not a data message */ #define SE_EOVERFLOW 75 /* Value too large for defined data type */ #define SE_ENOTUNIQ 76 /* Name not unique on network */ #define SE_EBADFD 77 /* File descriptor in bad state */ #define SE_EREMCHG 78 /* Remote address changed */ #define SE_ELIBACC 79 /* Can not access a needed shared library */ #define SE_ELIBBAD 80 /* Accessing a corrupted shared library */ #define SE_ELIBSCN 81 /* .lib section in a.out corrupted */ #define SE_ELIBMAX 82 /* Attempting to link in too many shared libraries */ #define SE_ELIBEXEC 83 /* Cannot exec a shared library directly */ #define SE_EILSEQ 84 /* Illegal byte sequence */ #define SE_ERESTART 85 /* Interrupted system call should be restarted */ #define SE_ESTRPIPE 86 /* Streams pipe error */ #define SE_EUSERS 87 /* Too many users */ #define SE_ENOTSOCK 88 /* Socket operation on non-socket */ #define SE_EDESTADDRREQ 89 /* Destination address required */ #define SE_EMSGSIZE 90 /* Message too long */ #define SE_EPROTOTYPE 91 /* Protocol wrong type for socket */ #define SE_ENOPROTOOPT 92 /* Protocol not available */ #define SE_EPROTONOSUPPORT 93 /* Protocol not supported */ #define SE_ESOCKTNOSUPPORT 94 /* Socket type not supported */ #define SE_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ #define SE_EPFNOSUPPORT 96 /* Protocol family not supported */ #define SE_EAFNOSUPPORT 97 /* Address family not supported by protocol */ #define SE_EADDRINUSE 98 /* Address already in use */ #define SE_EADDRNOTAVAIL 99 /* Cannot assign requested address */ #define SE_ENETDOWN 100 /* Network is down */ #define SE_ENETUNREACH 101 /* Network is unreachable */ #define SE_ENETRESET 102 /* Network dropped connection because of reset */ #define SE_ECONNABORTED 103 /* Software caused connection abort */ #define SE_ECONNRESET 104 /* Connection reset by peer */ #define SE_ENOBUFS 105 /* No buffer space available */ #define SE_EISCONN 106 /* Transport endpoint is already connected */ #define SE_ENOTCONN 107 /* Transport endpoint is not connected */ #define SE_ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ #define SE_ETOOMANYREFS 109 /* Too many references: cannot splice */ #define SE_ETIMEDOUT 110 /* Connection timed out */ #define SE_ECONNREFUSED 111 /* Connection refused */ #define SE_EHOSTDOWN 112 /* Host is down */ #define SE_EHOSTUNREACH 113 /* No route to host */ #define SE_EALREADY 114 /* Operation already in progress */ #define SE_EINPROGRESS 115 /* Operation now in progress */ #define SE_ESTALE 116 /* Stale NFS file handle */ #define SE_EUCLEAN 117 /* Structure needs cleaning */ #define SE_ENOTNAM 118 /* Not a XENIX named type file */ #define SE_ENAVAIL 119 /* No XENIX semaphores available */ #define SE_EISNAM 120 /* Is a named type file */ #define SE_EREMOTEIO 121 /* Remote I/O error */ #define SE_EDQUOT 122 /* Quota exceeded */ #define SE_ENOMEDIUM 123 /* No medium found */ #define SE_EMEDIUMTYPE 124 /* Wrong medium type */ #define SE_ECANCELED 125 #define SE_ERESTARTSYS 512 /* Interrupted system call */ #define SE_ERESTARTNOINTR 513 #define SE_ERESTARTNOHAND 514 /* restart if no handler.. */ #define SE_ENOIOCTLCMD 515 /* No ioctl command */ #define SE_ERESTART_RESTARTBLOCK 516 /* restart by calling sys_restart_syscall */ /* Defined for the NFSv3 protocol */ #define SE_EBADHANDLE 521 /* Illegal NFS file handle */ #define SE_ENOTSYNC 522 /* Update synchronization mismatch */ #define SE_EBADCOOKIE 523 /* Cookie is stale */ #define SE_ENOTSUPP 524 /* Operation is not supported */ #define SE_ETOOSMALL 525 /* Buffer or request is too small */ #define SE_ESERVERFAULT 526 /* An untranslatable error occurred */ #define SE_EBADTYPE 527 /* Type not supported by server */ #define SE_EJUKEBOX 528 /* Request initiated, but will not complete before timeout */ #define SE_EIOCBQUEUED 529 /* iocb queued, will get completion event */ #define SE_EIOCBRETRY 530 /* iocb queued, will trigger a retry */ sysdig-0.26.4/userspace/libsinsp/sinsp_inet.h000066400000000000000000000012661352731327100212570ustar00rootroot00000000000000/* Copyright (C) 2019 Sysdig, Inc. This file is part of sysdig. 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. */ #pragma once #if defined(_WIN32) # include #else # include #endif sysdig-0.26.4/userspace/libsinsp/sinsp_int.h000066400000000000000000000072551352731327100211160ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ //////////////////////////////////////////////////////////////////////////// // Public definitions for the scap library //////////////////////////////////////////////////////////////////////////// #pragma once #ifdef _WIN32 #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; #include "../libscap/scap.h" #include "settings.h" #include "utils.h" #include "../libscap/scap.h" #include "parsers.h" #include "ifinfo.h" #include "internal_metrics.h" #include "sinsp_public.h" #ifndef MIN #define MIN(X,Y) ((X) < (Y)? (X):(Y)) #define MAX(X,Y) ((X) > (Y)? (X):(Y)) #endif // // ASSERT implementation // #ifdef _DEBUG #undef ASSERT #ifdef ASSERT_TO_LOG #define ASSERT(X) \ if(!(X)) \ { \ g_logger.format(sinsp_logger::SEV_ERROR, "ASSERTION %s at %s:%d", #X , __FILE__, __LINE__); \ assert(X); \ } #else #define ASSERT(X) assert(X) #endif // ASSERT_TO_LOG #else // _DEBUG #define ASSERT(X) #endif // _DEBUG // // Public export macro // #ifdef _WIN32 #define BRK(X) {if(evt != NULL && evt->get_num() == X)__debugbreak();} #else #define BRK(X) #endif // // Path separator // #ifdef _WIN32 #define DIR_PATH_SEPARATOR '\\' #else #define DIR_PATH_SEPARATOR '/' #endif // // The logger // extern sinsp_logger g_logger; #define glogf g_logger.format // // Prototype of the callback invoked by the thread table when a thread is // created or destroyed // class sinsp_threadtable_listener { public: virtual ~sinsp_threadtable_listener() { } virtual void on_thread_created(sinsp_threadinfo* tinfo) = 0; virtual void on_thread_destroyed(sinsp_threadinfo* tinfo) = 0; }; // // Prototype of the callback invoked by the thread table when a thread is // created or destroyed // class sinsp_fd_listener { public: virtual ~sinsp_fd_listener() { } virtual void on_read(sinsp_evt* evt, int64_t tid, int64_t fd, sinsp_fdinfo_t* fdinfo, char *data, uint32_t original_len, uint32_t len) = 0; virtual void on_write(sinsp_evt* evt, int64_t tid, int64_t fd, sinsp_fdinfo_t* fdinfo, char *data, uint32_t original_len, uint32_t len) = 0; virtual void on_sendfile(sinsp_evt* evt, int64_t fdin, uint32_t len) = 0; virtual void on_connect(sinsp_evt* evt, uint8_t* packed_data) = 0; virtual void on_accept(sinsp_evt* evt, int64_t newfd, uint8_t* packed_data, sinsp_fdinfo_t* new_fdinfo) = 0; virtual void on_file_open(sinsp_evt* evt, const string& fullpath, uint32_t flags) = 0; virtual void on_error(sinsp_evt* evt) = 0; virtual void on_erase_fd(erase_fd_params* params) = 0; virtual void on_socket_shutdown(sinsp_evt *evt) = 0; virtual void on_execve(sinsp_evt* evt) = 0; virtual void on_clone(sinsp_evt* evt, sinsp_threadinfo* newtinfo) = 0; virtual void on_bind(sinsp_evt* evt) = 0; virtual bool on_resolve_container(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) = 0; virtual void on_socket_status_changed(sinsp_evt *evt) = 0; }; sysdig-0.26.4/userspace/libsinsp/sinsp_pd_callback_type.h000066400000000000000000000014201352731327100235700ustar00rootroot00000000000000/* Copyright (C) 2019 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once // // Protocol decoder callback type // typedef enum sinsp_pd_callback_type { CT_OPEN, CT_CONNECT, CT_READ, CT_WRITE, CT_TUPLE_CHANGE, }sinsp_pd_callback_type; sysdig-0.26.4/userspace/libsinsp/sinsp_public.h000066400000000000000000000013011352731327100215640ustar00rootroot00000000000000/* Copyright (C) 2018-2019 Sysdig, Inc. This file is part of sysdig. 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. */ #pragma once #ifdef _WIN32 #define SINSP_PUBLIC __declspec(dllexport) #else #define SINSP_PUBLIC #endif sysdig-0.26.4/userspace/libsinsp/sinsp_signal.h000066400000000000000000000032011352731327100215640ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #define SE_NSIG 64 #define SE_SIGHUP 1 #define SE_SIGINT 2 #define SE_SIGQUIT 3 #define SE_SIGILL 4 #define SE_SIGTRAP 5 #define SE_SIGABRT 6 #define SE_SIGIOT 6 #define SE_SIGBUS 7 #define SE_SIGFPE 8 #define SE_SIGKILL 9 #define SE_SIGUSR1 10 #define SE_SIGSEGV 11 #define SE_SIGUSR2 12 #define SE_SIGPIPE 13 #define SE_SIGALRM 14 #define SE_SIGTERM 15 #define SE_SIGSTKFLT 16 #define SE_SIGCHLD 17 #define SE_SIGCONT 18 #define SE_SIGSTOP 19 #define SE_SIGTSTP 20 #define SE_SIGTTIN 21 #define SE_SIGTTOU 22 #define SE_SIGURG 23 #define SE_SIGXCPU 24 #define SE_SIGXFSZ 25 #define SE_SIGVTALRM 26 #define SE_SIGPROF 27 #define SE_SIGWINCH 28 #define SE_SIGIO 29 #define SE_SIGPOLL SE_SIGIO #define SE_SIGPWR 30 #define SE_SIGSYS 31 #define SE_SIGUNUSED 31 sysdig-0.26.4/userspace/libsinsp/sinsp_test.cpp000066400000000000000000000020511352731327100216230ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "sinsp.h" #include "../../driver/ppm_events_public.h" TEST(inspector,get_proc_by_invalid_tid) { sinsp inspector; EXPECT_TRUE(NULL == inspector.get_process(-100)); } TEST(inspector,get_proc_by_valid_tid) { sinsp inspector; EXPECT_TRUE(NULL == inspector.get_process(-100)); sinsp_procinfo newpi(&inspector); newpi.m_tgid = -100; inspector.m_proctable[-100] = newpi; EXPECT_TRUE(NULL != inspector.get_process(-100)); } sysdig-0.26.4/userspace/libsinsp/socket_collector.h000066400000000000000000000212611352731327100224370ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // socket_collector.h // #pragma once #ifdef HAS_CAPTURE #include "socket_handler.h" template class socket_collector { public: typedef std::map> socket_map_t; socket_collector(bool do_loop = false, long timeout_ms = 1000L): m_nfds(0), m_loop(do_loop), m_timeout_ms(timeout_ms), m_stopped(false) { clear_fds(); } ~socket_collector() { } void add(std::shared_ptr handler) { if(handler) { int sockfd = handler->get_socket(m_timeout_ms); m_sockets[sockfd] = handler; g_logger.log("Socket collector: handler [" + handler->get_id() + "] added socket (" + std::to_string(sockfd) + ')', sinsp_logger::SEV_TRACE); } else { g_logger.log("Socket collector: attempt to add null handler.", sinsp_logger::SEV_ERROR); } } bool is_enabled(std::shared_ptr handler) const { if(handler) { return handler->is_enabled(); } return false; } void enable(std::shared_ptr handler) { if(handler) { handler->enable(); return; } g_logger.log("Socket collector: attempt to enable non-existing handler.", sinsp_logger::SEV_ERROR); } int get_socket(std::shared_ptr handler) const { for(const auto& http : m_sockets) { if(http.second == handler) { return http.first; } } return -1; } bool has(std::shared_ptr handler) { for(const auto& http : m_sockets) { if(http.second == handler) { return true; } } return false; } bool remove(std::shared_ptr handler) { for(typename socket_map_t::iterator it = m_sockets.begin(); it != m_sockets.end(); ++it) { if(it->second == handler) { remove(it); return true; } } return false; } void remove_all() { clear_fds(); m_sockets.clear(); m_nfds = 0; } int subscription_count() const { return m_sockets.size(); } int signaled_sockets_count() { int count = 0; for(typename socket_map_t::iterator it = m_sockets.begin(); it != m_sockets.end(); ++it) { if(FD_ISSET(it->first, &m_infd)) { ++count; } } return count; } void trace_sockets() { if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { for(typename socket_map_t::iterator it = m_sockets.begin(); it != m_sockets.end(); ++it) { if(it->second) { if(it->second->is_enabled()) { g_logger.log("Socket collector: examining socket " + std::to_string(it->first) + " (" + it->second->get_id() + ')', sinsp_logger::SEV_TRACE); if(FD_ISSET(it->first, &m_infd)) { g_logger.log("Socket collector: activity on socket " + std::to_string(it->first) + " (" + it->second->get_id() + ')', sinsp_logger::SEV_TRACE); } } else { g_logger.log("Socket collector: socket " + std::to_string(it->first) + " handler (" + it->second->get_id() + ") is not enabled.", sinsp_logger::SEV_TRACE); } } else { g_logger.log("Socket collector: socket " + std::to_string(it->first) + " handler (" + it->second->get_id() + ") is null.", sinsp_logger::SEV_TRACE); } } } } bool is_fd_valid(int sockfd) { struct timeval tv = {0}; fd_set infd, outfd, errfd; FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd); FD_SET(sockfd, &infd); FD_SET(sockfd, &outfd); FD_SET(sockfd, &errfd); return 0 <= select(sockfd + 1, &infd, &outfd, &errfd, &tv); } void enable_sockets() { clear_fds(); for(typename socket_map_t::iterator it = m_sockets.begin(); it != m_sockets.end();) { int sockfd = -1; if(it->second) { if(it->second->is_enabled()) { if(!is_fd_valid(it->first)) { remove(it); continue; } else { sockfd = it->first; FD_SET(sockfd, &m_infd); FD_SET(sockfd, &m_errfd); if(sockfd > m_nfds) { m_nfds = sockfd; } } } } ++it; } } void get_data() { try { struct timeval tv; int res; m_stopped = false; while(!m_stopped) { tv.tv_sec = m_loop ? m_timeout_ms / 1000 : 0; tv.tv_usec = m_loop ? (m_timeout_ms % 1000) * 1000 : 0; { if(m_sockets.size()) { enable_sockets(); // flag all enabled handler sockets g_logger.log("Socket collector: total sockets=" + std::to_string(m_sockets.size()) + ", select-enabled sockets= " + std::to_string(signaled_sockets_count()), sinsp_logger::SEV_TRACE); res = select(m_nfds + 1, &m_infd, NULL, &m_errfd, &tv); g_logger.log("Socket collector: total sockets=" + std::to_string(m_sockets.size()) + ", signaled sockets= " + std::to_string(signaled_sockets_count()), sinsp_logger::SEV_TRACE); if(res == 0) // all quiet { g_logger.log("Socket collector: " + std::to_string(m_sockets.size()) + " sockets total, no activity.", sinsp_logger::SEV_DEBUG); } else if(res < 0) // select error { // socket sets are undefined after error, nothing to do here ... throw sinsp_exception(std::string("Socket collector: select error (").append(strerror(errno)).append(1, ')')); } else // data available or socket error { trace_sockets(); for(typename socket_map_t::iterator it = m_sockets.begin(); it != m_sockets.end();) { std::string id = it->second->get_id(); int err = 0; if(FD_ISSET(it->first, &m_infd)) { if(it->second && it->second->is_enabled() && (err = it->second->on_data())) { if((err != EAGAIN) && (err != EINPROGRESS)) { if(err != it->second->CONNECTION_CLOSED) { g_logger.log("Socket collector: data handling error " + std::to_string(errno) + ", (" + strerror(errno) + "), removing handler [" + id + ']', sinsp_logger::SEV_ERROR); } else { g_logger.log("Socket collector: connection close detected while handling data" ", removing handler [" + id + ']', sinsp_logger::SEV_DEBUG); } remove(it); continue; } } } if(FD_ISSET(it->first, &m_errfd)) { if(it->second && (err = it->second->get_socket_error())) { g_logger.log("Socket collector: socket error " + std::to_string(err) + ", (" + strerror(err) + "), removing handler [" + id + ']', sinsp_logger::SEV_ERROR); } else { g_logger.log("Socket collector: handler [" + id + "] unknown socket error, closing connection.", sinsp_logger::SEV_ERROR); } remove(it); continue; } ++it; } } } else { g_logger.log("Socket collector is empty.", sinsp_logger::SEV_DEBUG); m_stopped = true; return; } } if(!m_loop) { break; } } } catch(std::exception& ex) { g_logger.log(std::string("Socket collector error: ") + ex.what(), sinsp_logger::SEV_ERROR); remove_all(); m_stopped = true; } } void stop() { m_stopped = true; } bool is_active() const { return subscription_count() > 0; } bool is_healthy(std::shared_ptr handler) const { if(m_steady_state) { return get_socket(handler) != -1; } return true; } // flag indicating collector passed through the // transitional state (if any), where sockets and // handlers may come and go void set_steady_state(bool state = true) { m_steady_state = true; } bool get_steady_state() const { return m_steady_state; } private: void clear_fds() { FD_ZERO(&m_errfd); FD_ZERO(&m_infd); } typename socket_map_t::iterator& remove(typename socket_map_t::iterator& it) { if(it != m_sockets.end()) { m_sockets.erase(it++); } m_nfds = 0; for(const auto& sock : m_sockets) { if(sock.first > m_nfds) { m_nfds = sock.first; } } return it; } socket_map_t m_sockets; fd_set m_infd; fd_set m_errfd; int m_nfds = 0; bool m_loop = false; long m_timeout_ms; bool m_stopped = false; bool m_steady_state = false; }; #endif // HAS_CAPTURE sysdig-0.26.4/userspace/libsinsp/socket_handler.h000066400000000000000000001425171352731327100220760ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // socket_handler.h // #pragma once #ifdef HAS_CAPTURE #include "http_parser.h" #include "uri.h" #include "json/json.h" #define BUFFERSIZE 512 // b64 needs this macro #include "b64/encode.h" #include "sinsp.h" #include "sinsp_int.h" #include "sinsp_auth.h" #include "http_reason.h" #include "json_query.h" #include #include #include #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #ifndef SOCK_NONBLOCK #define SOCK_NONBLOCK 0 #endif struct gaicb_free { void operator() (struct gaicb **reqs) const { if(reqs[0]->ar_result) { freeaddrinfo(reqs[0]->ar_result); } if(reqs[0]->ar_name) { free((void*)reqs[0]->ar_name); } free(reqs[0]); free(reqs); } }; template class socket_data_handler { public: typedef std::shared_ptr ptr_t; typedef std::shared_ptr json_ptr_t; typedef sinsp_ssl::ptr_t ssl_ptr_t; typedef sinsp_bearer_token::ptr_t bt_ptr_t; typedef void (T::*json_callback_func_t)(json_ptr_t, const std::string&); static const std::string HTTP_VERSION_10; static const std::string HTTP_VERSION_11; static const int CONNECTION_CLOSED = ~0; socket_data_handler(T& obj, const std::string& id, const std::string& url, const std::string& path = "", const std::string& http_version = HTTP_VERSION_11, int timeout_ms = 1000, ssl_ptr_t ssl = nullptr, bt_ptr_t bt = nullptr, bool keep_alive = true, bool blocking = false, unsigned data_limit = 524288u, bool fetching_state = true): m_obj(obj), m_id(id), m_url(url), m_keep_alive(keep_alive ? std::string("Connection: keep-alive\r\n") : std::string()), m_path(path.empty() ? m_url.get_path() : path), m_blocking(blocking), m_ssl(ssl), m_bt(bt), m_timeout_ms(timeout_ms), m_request(make_request(url, http_version)), m_http_version(http_version), m_data_limit(data_limit), m_fetching_state(fetching_state) { g_logger.log(std::string("Creating Socket handler object for (" + id + ") " "[" + uri(url).to_string(false) + ']'), sinsp_logger::SEV_DEBUG); m_buf.resize(1024); init_http_parser(); } virtual ~socket_data_handler() { cleanup(); } virtual int get_socket(long timeout_ms = -1) { if(timeout_ms != -1) { m_timeout_ms = timeout_ms; } if(m_socket < 0 || !m_connected) { connect_socket(); } return m_socket; } virtual bool is_connected() const { return m_connected; } bool is_connecting() { if(m_address.empty()) { try_resolve(); } if(!m_connected && m_sa && m_sa_len) { try_connect(); } return m_connecting; } void close_on_chunked_end(bool close = true) { m_close_on_chunked_end = close; } const uri& get_url() const { return m_url; } void set_path(const std::string& path) { m_path = path; m_request = make_request(m_url, m_http_version); } std::string make_request(uri url, const std::string& http_version) { std::ostringstream request; std::string host_and_port = url.get_host(); if(!host_and_port.empty()) { int port = url.get_port(); if(port) { host_and_port.append(1, ':').append(std::to_string(port)); } } request << "GET " << m_path; std::string query = url.get_query(); if(!query.empty()) { request << '?' << query; } request << " HTTP/" << http_version << "\r\n" << m_keep_alive << "User-Agent: sysdig\r\n"; if(!host_and_port.empty()) { request << "Host: " << host_and_port << "\r\n"; } request << "Accept: */*\r\n"; std::string creds = url.get_credentials(); if(!creds.empty()) { uri::decode(creds); std::istringstream is(creds); std::ostringstream os; base64::encoder().encode(is, os); std::string bauth = os.str(); request << "Authorization: Basic " << trim(bauth) << "\r\n"; } if(m_bt && !m_bt->get_token().empty()) { request << "Authorization: Bearer " << m_bt->get_token() << "\r\n"; } request << "\r\n"; return request.str(); } void set_id(const std::string& id) { m_id = id; } const std::string& get_id() const { return m_id; } void set_json_callback(json_callback_func_t f) { m_json_callback = f; } SSL* ssl_connection() { return m_ssl_connection; } bool wants_send() const { return m_wants_send; } void send_request() { m_wants_send = false; // no matter what happens, this is a one-shot if(m_request.empty()) { throw sinsp_exception("Socket handler (" + m_id + ") send: request (empty)."); } if(m_socket <= 0) { throw sinsp_exception("Socket handler (" + m_id + ") send: invalid socket."); } int iolen = 0; if(m_request.size()) { g_logger.log("Socket handler (" + m_id + ") socket=" + std::to_string(m_socket) + ", m_ssl_connection=" + std::to_string((int64_t)m_ssl_connection), sinsp_logger::SEV_TRACE); std::string req = m_request; time_t then; time(&then); while(req.size()) { if(m_url.is_secure()) { if(!m_ssl_connection) { throw sinsp_exception("Socket handler (" + m_id + ") send: SSL connection is null."); } iolen = SSL_write(m_ssl_connection, m_request.c_str(), m_request.size()); } else { iolen = send(m_socket, m_request.c_str(), m_request.size(), 0); } if(iolen == static_cast(req.size())) { break; } else if(iolen == 0 || errno == ENOTCONN || errno == EPIPE) { goto connection_closed; } else if(iolen < 0) { if(errno == ENOTCONN || errno == EPIPE) { goto connection_closed; } else if(errno != EAGAIN && errno != EWOULDBLOCK) { goto connection_error; } if(m_url.is_secure()) { int err = SSL_get_error(m_ssl_connection, iolen); if(err != SSL_ERROR_WANT_WRITE && err != SSL_ERROR_WANT_READ) { goto connection_error; } } } else { req.erase(0, iolen); } time_t now; time(&now); if(difftime(now, then) > m_timeout_ms * 1000) { throw sinsp_exception("Socket handler (" + m_id + "): send timeout."); } } } else { throw sinsp_exception("Socket handler (" + m_id + ") request is empty."); } g_logger.log(m_request, sinsp_logger::SEV_TRACE); return; connection_error: { std::string err = strerror(errno); std::ostringstream os; os << "Socket handler (" << m_id << ") send_request(), connection [" << m_url.to_string(false) << "] error: " << err; if(m_url.is_secure()) { std::string ssl_err = ssl_errors(); if(!ssl_err.empty()) { os << std::endl << "SSL error: " << ssl_err; } } throw sinsp_exception(os.str()); } connection_closed: { std::ostringstream os; os << "Socket handler (" << m_id << ") send_request(), connection [" << m_url.to_string(false) << "] closed."; if(m_url.is_secure()) { std::string ssl_err = ssl_errors(); if(!ssl_err.empty()) { os << std::endl << "SSL error: " << ssl_err; } } m_connecting = false; m_connected = false; throw sinsp_exception(os.str()); } } void set_socket_option(int opt) { int flags = fcntl(m_socket, F_GETFL, 0); if(flags != -1) { fcntl(m_socket, F_SETFL, flags | opt); } else { throw sinsp_exception("Socket handler (" + m_id + ") error while setting socket option (" + std::to_string(opt) + "): " + strerror(errno)); } } int get_all_data() { g_logger.log("Socket handler (" + m_id + ") Retrieving all data in blocking mode ...", sinsp_logger::SEV_TRACE); ssize_t rec = 0; std::vector buf(1024, 0); int counter = 0; int processed = 0; init_http_parser(); do { int count = 0; int ioret = ioctl(m_socket, FIONREAD, &count); if(ioret >= 0 && count > 0) { buf.resize(count); if(m_url.is_secure()) { rec = SSL_read(m_ssl_connection, &buf[0], buf.size()); } else { rec = recv(m_socket, &buf[0], buf.size(), 0); } if(rec > 0) { process(&buf[0], rec, false); processed += rec; } else if(rec == 0) { throw sinsp_exception("Socket handler (" + m_id + "): Connection closed."); } else if(rec < 0) { throw sinsp_exception("Socket handler (" + m_id + "): " + strerror(errno)); } //g_logger.log("Socket handler (" + m_id + ") received=" + std::to_string(rec) + // "\n\n" + data + "\n\n", sinsp_logger::SEV_TRACE); } // To prevent reads from entirely stalling (like in gigantic k8s // environments), give up after reading 30mb. ++counter; if(processed > 30 * 1024 * 1024) { throw sinsp_exception("Socket handler (" + m_id + "): " "read more than 30MB of data from " + m_url.to_string(false) + m_path + " (" + std::to_string(processed) + " bytes, " + std::to_string(counter) + " reads). Giving up"); } else { usleep(10000); } } while(!m_msg_completed); init_http_parser(); return processed; } void data_handling_error(const std::string& data, size_t nparsed) { std::ostringstream os; os << "Socket handler (" << m_id + ") an error occurred during http parsing. " "processed=" << nparsed << ", expected=" << data.size() << ", status_code=" << std::to_string(m_http_parser->status_code) << ", http_errno=" << std::to_string(m_http_parser->http_errno) << "data:" << std::endl << data; throw sinsp_exception(os.str()); } void parse_http(char* data, size_t len) { size_t nparsed = http_parser_execute(m_http_parser, &m_http_parser_settings, data, len); if(nparsed != len) { data_handling_error(std::string(data, len), nparsed); } } void process_json() { if(m_json_filters.empty()) { add_json_filter("."); } bool handled = false; for(auto js = m_json.begin(); js != m_json.end();) { handled = false; for(auto it = m_json_filters.cbegin(); it != m_json_filters.cend(); ++it) { json_ptr_t pjson = try_parse(m_jq, *js, *it, m_id, m_url.to_string(false)); if(pjson) { (m_obj.*m_json_callback)(pjson, m_id); handled = true; break; } } if(!handled) { g_logger.log("Socket handler: (" + m_id + ") JSON not handled, " "discarding:\n" + *js, sinsp_logger::SEV_ERROR); } js = m_json.erase(js); } } int process(char* data, size_t len, bool reinit = true) { if(len) { parse_http(data, len); unsigned parser_errno = HTTP_PARSER_ERRNO(m_http_parser); if(parser_errno != HPE_OK) { if(parser_errno <= HPE_UNKNOWN) { g_logger.log("Socket handler (" + m_id + ") http parser error " + std::to_string(parser_errno) + " ([" + http_errno_name((http_errno) parser_errno) + "]: " + http_errno_description((http_errno) parser_errno) + ')', sinsp_logger::SEV_ERROR); } else { g_logger.log("Socket handler (" + m_id + ") http parser error " + std::to_string(parser_errno) + ')', sinsp_logger::SEV_ERROR); } return CONNECTION_CLOSED; } if(m_json.size()) { process_json(); } if(m_http_response >= 400) { g_logger.log("Socket handler (" + m_id + ") response " + std::to_string(m_http_response) + " (" + get_http_reason(m_http_response) + ") received, disconnecting ... ", sinsp_logger::SEV_ERROR); return CONNECTION_CLOSED; } if(m_msg_completed) { if(m_data_buf.size()) // should never happen { g_logger.log("Socket handler (" + m_id + ") response ended with unprocessed data, " "clearing and sending new request ... ", sinsp_logger::SEV_WARNING); ASSERT(!m_data_buf.size()); m_data_buf.clear(); } // In HTTP 1.1 connnections with chunked transfer, this socket may never be closed by server, // (K8s API server is an example of such behavior), in which case the chunked data will just // stop flowing. We can keep the good socket and resend the request instead of severing the // connection. The m_wants_send flag has to be checked by the caller and request re-sent, otherwise // this pipeline will remain idle. To force client-initiated socket close on chunked transfer end, // set the m_close_on_chunked_end flag to true (default). if(m_close_on_chunked_end) { g_logger.log("Socket handler (" + m_id + ") chunked response ended", sinsp_logger::SEV_DEBUG); return CONNECTION_CLOSED; } m_wants_send = true; if(reinit) { init_http_parser(); } } } return 0; } int on_data() { bool is_error = false; if(!m_json_callback) { throw sinsp_exception("Socket handler (" + m_id + "): cannot parse data (callback is null)."); } ssize_t iolen = 0; size_t len_read = 0, len_to_read = m_buf.size(); try { do { if(len_read >= m_data_limit) { break; } else if((len_read + m_buf.size()) > m_data_limit) { len_to_read = m_data_limit - len_read; } errno = 0; if(m_url.is_secure()) { iolen = static_cast(SSL_read(m_ssl_connection, &m_buf[0], len_to_read)); } else { iolen = recv(m_socket, &m_buf[0], len_to_read, 0); } if(iolen > 0) { len_read += iolen; } m_sock_err = errno; sinsp_logger::severity sev = (iolen < 0 && m_sock_err != EAGAIN) ? sinsp_logger::SEV_DEBUG : sinsp_logger::SEV_TRACE; g_logger.log("Socket handler (" + m_id + ") " + m_url.to_string(false) + ", iolen=" + std::to_string(iolen) + ", data=" + std::to_string(len_read) + " bytes, " "errno=" + std::to_string(m_sock_err) + " (" + strerror(m_sock_err) + ')', sev); /* uncomment to see raw HTTP stream data in trace logs if((iolen > 0) && g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log("Socket handler (" + m_id + "), data --->" + std::string(&m_buf[0], iolen) + "<--- data", sinsp_logger::SEV_TRACE); } */ if(iolen > 0) { size_t len = (iolen <= static_cast(m_buf.size())) ? static_cast(iolen) : m_buf.size(); if(CONNECTION_CLOSED == process(&m_buf[0], len)) { return CONNECTION_CLOSED; } } else if(iolen == 0 || m_sock_err == ENOTCONN || m_sock_err == EPIPE) { if(m_url.is_secure()) { if(m_ssl_connection) { int err = SSL_get_error(m_ssl_connection, iolen); if (err != SSL_ERROR_ZERO_RETURN) { g_logger.log("Socket handler(" + m_id + "): SSL conn closed with code " + std::to_string(err), sinsp_logger::SEV_DEBUG); } int sd = SSL_get_shutdown(m_ssl_connection); if(sd == 0) { g_logger.log("Socket handler (" + m_id + "): SSL zero bytes received, " "but no shutdown state set for [" + m_url.to_string(false) + "]: ", sinsp_logger::SEV_WARNING); } if(sd & SSL_RECEIVED_SHUTDOWN) { g_logger.log("Socket handler(" + m_id + "): SSL shutdown from [" + m_url.to_string(false) + "]: ", sinsp_logger::SEV_TRACE); } if(sd & SSL_SENT_SHUTDOWN) { g_logger.log("Socket handler(" + m_id + "): SSL shutdown sent to [" + m_url.to_string(false) + "]: ", sinsp_logger::SEV_TRACE); } } else { g_logger.log("Socket handler(" + m_id + "): SSL connection is null", sinsp_logger::SEV_WARNING); } } goto connection_closed; } else if(iolen < 0) { if(m_sock_err == ENOTCONN || m_sock_err == EPIPE) { goto connection_closed; } else if(m_sock_err != EAGAIN && m_sock_err != EWOULDBLOCK) { goto connection_error; } if(m_url.is_secure()) { int err = SSL_get_error(m_ssl_connection, iolen); if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { g_logger.log("Socket handler(" + m_id + "): received SSL error" + std::to_string(err), sinsp_logger::SEV_ERROR); goto connection_error; } } } } while(iolen && (m_sock_err != EAGAIN) && (len_read < m_data_limit)); g_logger.log("Socket handler (" + m_id + ") " + std::to_string(len_read) + " bytes of data received", sinsp_logger::SEV_TRACE); } catch(sinsp_exception& ex) { g_logger.log(std::string("Socket handler (" + m_id + ") data receive error [" + m_url.to_string(false) + "]: ").append(ex.what()), sinsp_logger::SEV_ERROR); return m_sock_err; } return 0; connection_error: is_error = true; connection_closed: if(m_url.is_secure()) { std::string ssl_err = ssl_errors(); if(!ssl_err.empty()) { g_logger.log(ssl_err, sinsp_logger::SEV_ERROR); } } return is_error ? m_sock_err : CONNECTION_CLOSED; } void on_error(const std::string& /*err*/, bool /*disconnect*/) { } bool has_json_filter(const std::string& filter) { for(auto flt : m_json_filters) { if(flt == filter) { return true; } } return false; } void add_json_filter(const std::string& filter, const std::string& before_filter = "") { if(filter.empty()) { throw sinsp_exception(std::string("Socket handler (") + m_id + "), " "[" + m_url.to_string(false) + "] " "attempt to add empty filter"); } remove_json_filter(filter); if(before_filter.empty()) { m_json_filters.push_back(filter); return; } else { auto it = m_json_filters.begin(); for(; it != m_json_filters.end(); ++it) { if(*it == before_filter) { break; } } if(it == m_json_filters.end()) { g_logger.log("Socket handler (" + m_id + "), [" + m_url.to_string(false) + "] " "attempt to insert filter before a non-existing filter. " "Filter will be added to the end of filter list.", sinsp_logger::SEV_WARNING); } m_json_filters.insert(it, filter); } } void remove_json_filter(const std::string& filter) { for(auto it = m_json_filters.begin(); it != m_json_filters.end(); ++it) { if(*it == filter) { m_json_filters.erase(it); return; } } } void replace_json_filter(const std::string& from, const std::string& to) { for(auto it = m_json_filters.begin(); it != m_json_filters.end(); ++it) { if(*it == from) { *it = to; return; } } throw sinsp_exception(std::string("Socket handler (") + m_id + "), " "[" + m_url.to_string(false) + "] " "attempt to replace non-existing filter"); } void print_filters(sinsp_logger::severity sev = sinsp_logger::SEV_DEBUG) { std::ostringstream filters; filters << std::endl << "Filters:" << std::endl; for(auto filter : m_json_filters) { filters << filter << std::endl; } g_logger.log("Socket handler (" + m_id + "), [" + m_url.to_string(false) + "]" + filters.str(), sev); } static json_ptr_t try_parse(json_query& jq, const std::string& json, const std::string& filter, const std::string& id, const std::string& url) { std::string filtered_json(json); if(!filter.empty()) { // failure to parse is ok, it will fail over to the next filter // and log error if all filters fail if(jq.process(json, filter)) { filtered_json = jq.result(); if (filtered_json.empty() && !jq.get_error().empty()) { g_logger.log("Socket handler (" + id + "), [" + url + "] filter result is empty \"" + jq.get_error() + "\"; JSON: <" + json + ">, jq filter: <" + filter + '>', sinsp_logger::SEV_DEBUG); } } else { g_logger.log("Socket handler (" + id + "), [" + url + "] filter processing error \"" + jq.get_error() + "\"; JSON: <" + json + ">, jq filter: <" + filter + '>', sinsp_logger::SEV_DEBUG); return nullptr; } } json_ptr_t root(new Json::Value()); try { if(Json::Reader().parse(filtered_json, *root)) { /* if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log("Socket handler (" + id + "), [" + url + "] " "filtered JSON: " + json_as_string(*root), sinsp_logger::SEV_TRACE); } */ return root; } } catch(...) { } g_logger.log("Socket handler (" + id + "), [" + url + "] parsing error; JSON: <" + json + ">, jq filter: <" + filter + '>', sinsp_logger::SEV_ERROR); return nullptr; } // when connection is non-blocking and a socket // should not be polled until it is connected // this flag indicates readiness to be polled bool is_enabled() const { return m_enabled; } void enable(bool e = true) { m_enabled = e; } bool connection_error() const { return m_connection_error; } int get_socket_error() { socklen_t optlen = sizeof(m_sock_err); int ret = getsockopt(m_socket, SOL_SOCKET, SO_ERROR, &m_sock_err, &optlen); g_logger.log("Socket handler (" + m_id + ") getsockopt() ret=" + std::to_string(ret) + ", m_sock_err=" + std::to_string(m_sock_err) + " (" + strerror(m_sock_err) + ')', sinsp_logger::SEV_TRACE); if(!ret) { return m_sock_err; } throw sinsp_exception("Socket handler (" + m_id + ") an error occurred " "trying to obtain socket status while connecting to " + m_url.to_string(false) + ": " + strerror(ret)); } private: typedef std::vector password_vec_t; int wait(bool for_recv, long tout = 1000L) { struct timeval tv; tv.tv_sec = m_timeout_ms / 1000; tv.tv_usec = (m_timeout_ms % 1000) * 1000; fd_set infd, outfd, errfd; FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd); FD_SET(m_socket, &errfd); if(for_recv) { FD_SET(m_socket, &infd); } else { FD_SET(m_socket, &outfd); } return select(m_socket + 1, &infd, &outfd, &errfd, &tv); } bool send_ready() { struct timeval tv = {0}; fd_set outfd; FD_ZERO(&outfd); FD_SET(m_socket, &outfd); int sel_ret = select(m_socket + 1, 0, &outfd, 0, &tv); if(sel_ret != 1) { return false; } int sock_ret = get_socket_error(); if(!sock_ret) { return true; } return false; } bool recv_ready() { struct timeval tv = {0}; fd_set infd; FD_ZERO(&infd); FD_SET(m_socket, &infd); return select(m_socket + 1, &infd, 0, 0, &tv) == 1; } bool socket_error() { struct timeval tv = {0}; fd_set errfd; FD_ZERO(&errfd); FD_SET(m_socket, &errfd); return select(m_socket + 1, 0, 0, &errfd, &tv) == 1; } static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX* ctx) { SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); if(ssl) { char buf[256] = {0}; X509* err_cert = X509_STORE_CTX_get_current_cert(ctx); X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); if(preverify_ok && SSL_get_verify_result(ssl) == X509_V_OK) { g_logger.log("Socket handler SSL CA verified: " + std::string(buf), sinsp_logger::SEV_DEBUG); return 1; } else { int err = X509_STORE_CTX_get_error(ctx); int depth = X509_STORE_CTX_get_error_depth(ctx); g_logger.log("Socket handler SSL CA verify error: num=" + std::to_string(err) + ':' + X509_verify_cert_error_string(err) + ":depth=" + std::to_string(depth) + ':' + std::string(buf), sinsp_logger::SEV_ERROR); return 0; } } return 0; } static int ssl_no_verify_callback(int, X509_STORE_CTX* ctx) { g_logger.log("Socket handler SSL CA verification disabled, certificate accepted.", sinsp_logger::SEV_DEBUG); return 1; } static int ssl_key_password_cb(char *buf, int size, int, void* pass) { if(pass) { std::memset(buf, 0, size); int pass_len = static_cast(strlen((char*)pass)); if(size < (pass_len) + 1) { return 0; } strncpy(buf, (const char*)pass, pass_len); return pass_len; } return 0; } std::string ssl_errors() { std::ostringstream os; if(m_url.is_secure()) { char errbuf[256] = {0}; unsigned long err; while((err = ERR_get_error()) != 0) { if(os.str().empty()) { os << "Socket handler (" << m_id << ", " "socket=" << std::to_string(m_socket) << ") SSL errors:\n"; } os << ERR_error_string(err, errbuf) << std::endl; } } return os.str(); } void init_ssl_context(void) { if(!m_ssl_context) { SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); const SSL_METHOD* method = #if (OPENSSL_VERSION_NUMBER < 0x10100000L) TLSv1_2_client_method(); #else TLS_client_method(); #endif if(!method) { g_logger.log("Socket handler (" + m_id + "): Can't initialize SSL method\n" + ssl_errors(), sinsp_logger::SEV_ERROR); } m_ssl_context = SSL_CTX_new(method); if(!m_ssl_context) { g_logger.log("Socket handler (" + m_id + "): Can't initialize SSL context\n" + ssl_errors(), sinsp_logger::SEV_ERROR); return; } if(m_ssl) { if(m_ssl->verify_peer()) { const std::string ca_cert = m_ssl->ca_cert(); if(!ca_cert.empty() && !SSL_CTX_load_verify_locations(m_ssl_context, ca_cert.c_str(), 0)) { throw sinsp_exception("Socket handler (" + m_id + "): " "Can't load SSL CA certificate (" + ca_cert + ").\n" + ssl_errors()); } else if(ca_cert.empty()) { throw sinsp_exception("Socket handler (" + m_id + "): " "Invalid SSL CA certificate configuration " "(Verify Peer enabled but no CA certificate specified)."); } SSL_CTX_set_verify(m_ssl_context, SSL_VERIFY_PEER, ssl_verify_callback); g_logger.log("Socket handler (" + m_id + "): CA verify set to PEER", sinsp_logger::SEV_TRACE); } else { SSL_CTX_set_verify(m_ssl_context, SSL_VERIFY_NONE, ssl_no_verify_callback); g_logger.log("Socket handler (" + m_id + "): CA verify set to NONE", sinsp_logger::SEV_TRACE); } const std::string& cert = m_ssl->cert(); if(!cert.empty()) { if(SSL_CTX_use_certificate_file(m_ssl_context, cert.c_str(), SSL_FILETYPE_PEM) <= 0) { throw sinsp_exception("Socket handler (" + m_id + "): " "Can't load SSL certificate from " + cert + ".\n" + ssl_errors()); } else { g_logger.log("Socket handler (" + m_id + "): using SSL certificate from " + cert, sinsp_logger::SEV_TRACE); } const std::string& key = m_ssl->key(); if(!key.empty()) { const std::string& pass = m_ssl->key_passphrase(); if(!pass.empty()) { m_ssl_key_pass.assign(pass.begin(), pass.end()); m_ssl_key_pass.push_back('\0'); SSL_CTX_set_default_passwd_cb_userdata(m_ssl_context, (void*)&m_ssl_key_pass[0]); SSL_CTX_set_default_passwd_cb(m_ssl_context, ssl_key_password_cb); } if(SSL_CTX_use_PrivateKey_file(m_ssl_context, key.c_str(), SSL_FILETYPE_PEM) <= 0) { throw sinsp_exception("Socket handler (" + m_id + "): " "Can't load SSL private key from " + key + ".\n" + ssl_errors()); } else { g_logger.log("Socket handler (" + m_id + "): using SSL private key from " + key, sinsp_logger::SEV_TRACE); } if(!SSL_CTX_check_private_key(m_ssl_context)) { throw sinsp_exception("Socket handler (" + m_id + "): " "SSL private key (" + key + ") does not match public certificate (" + cert + ").\n" + ssl_errors()); } else { g_logger.log("Socket handler (" + m_id + "): SSL private key " + key + " matches public certificate " + cert, sinsp_logger::SEV_TRACE); } } else { throw sinsp_exception("Socket handler (" + m_id + "): " "Invalid SSL configuration: public certificate specified without private key."); } } else { g_logger.log("Socket handler (" + m_id + "): SSL public certificate not provided.", sinsp_logger::SEV_TRACE); } } } } void init_ssl_socket() { if(m_socket != -1 && !m_ssl_init_complete) { if(m_url.is_secure()) { if(!m_ssl_context) { init_ssl_context(); } if(m_ssl_context) { m_ssl_connection = SSL_new(m_ssl_context); if(m_ssl_connection) { if(1 == SSL_set_fd(m_ssl_connection, m_socket)) { m_ssl_init_complete = true; } else { throw sinsp_exception("Socket handler " + m_id + " (" + m_url.to_string(false) + ") " "error assigning socket to SSL connection: " + ssl_errors()); } } else { throw sinsp_exception("Socket handler " + m_id + " (" + m_url.to_string(false) + ") " "error obtaining socket: " + ssl_errors()); } } else { throw sinsp_exception("Socket handler " + m_id + " (" + m_url.to_string(false) + ") " "SSL context error : " + ssl_errors()); } } } } void create_socket() { int sock_type = SOCK_STREAM; if(!m_blocking) { sock_type |= SOCK_NONBLOCK; } if(m_socket < 0) { if(m_url.is_file()) { m_socket = socket(PF_UNIX, sock_type, 0); } else { m_socket = socket(PF_INET, sock_type, 0); } if(m_socket < 0) { throw sinsp_exception("Socket handler " + m_id + " (" + m_url.to_string(false) + ") " "error obtaining socket: " + strerror(errno)); } } } bool check_connected() { if(!send_ready()) { if(m_sock_err && m_sock_err != EINPROGRESS) { m_connection_error = true; throw sinsp_exception("Socket handler (" + m_id + ") an error occurred " "while connecting to " + m_url.to_string(false) + ": " + strerror(m_sock_err)); } else if(m_sock_err == EINPROGRESS) { m_connection_error = false; m_connecting = true; m_connected = false; } return false; } return true; } bool try_connect() { g_logger.log("Socket handler (" + m_id + ") try_connect() entry, m_connecting=" + std::to_string(m_connecting) + ", m_connected=" + std::to_string(m_connected), sinsp_logger::SEV_TRACE); if(m_connected) { return true; } if(m_socket == -1) { create_socket(); } if(!is_resolved()) { if(!try_resolve()) { return false; } } int ret = -1; if(m_connection_error) { return false; } else { if(!check_connected()) { return false; } } g_logger.log("Socket handler (" + m_id + ") try_connect() middle, m_connecting=" + std::to_string(m_connecting) + ", m_connected=" + std::to_string(m_connected), sinsp_logger::SEV_TRACE); if(!m_connected) { g_logger.log("Socket handler (" + m_id + ") connecting to " + m_url.to_string(false) + " (socket=" + std::to_string(m_socket) + ')', sinsp_logger::SEV_DEBUG); if(!m_sa || !m_sa_len) { std::ostringstream os; os << m_sa; throw sinsp_exception("Socket handler (" + m_id + ") invalid state connecting to " + m_url.to_string(false) + " (socket=" + std::to_string(m_socket) + "), " "sa=" + os.str() + ", sa_len=" + std::to_string(m_sa_len)); } if(!m_connect_called) { ret = connect(m_socket, m_sa, m_sa_len); m_connect_called = true; if(ret < 0 && errno != EINPROGRESS) { throw sinsp_exception("Error during connection attempt to " + m_url.to_string(false) + " (socket=" + std::to_string(m_socket) + ", error=" + std::to_string(errno) + "): " + strerror(errno)); } else if(errno == EINPROGRESS) { m_connecting = true; m_connected = false; return false; } } else { if(get_socket_error() == EINPROGRESS) { m_connecting = true; m_connected = false; return false; } } if(m_url.is_secure()) { if(!m_ssl_init_complete) { init_ssl_socket(); } if(m_ssl_connection) { ret = SSL_connect(m_ssl_connection); if(ret == 1) { m_connecting = false; m_connected = true; g_logger.log("Socket handler (" + m_id + "): " "SSL connected to " + m_url.get_host(), sinsp_logger::SEV_INFO); g_logger.log("Socket handler (" + m_id + "): " "SSL socket=" + std::to_string(m_socket) + ", " "local port=" + std::to_string(get_local_port()), sinsp_logger::SEV_DEBUG); } else { int err = SSL_get_error(m_ssl_connection, ret); switch(err) { case SSL_ERROR_NONE: // 0 break; case SSL_ERROR_SSL: // 1 throw sinsp_exception(ssl_errors()); case SSL_ERROR_WANT_READ: // 2 case SSL_ERROR_WANT_WRITE: // 3 return false; case SSL_ERROR_WANT_X509_LOOKUP: // 4 break; case SSL_ERROR_SYSCALL: // 5 throw sinsp_exception("Socket handler (" + m_id + "), error " + std::to_string(err) + " (" + strerror(errno) + ") while connecting to " + m_url.get_host() + ':' + std::to_string(m_url.get_port())); case SSL_ERROR_ZERO_RETURN: // 6 cleanup(); throw sinsp_exception("Socket handler (" + m_id + "), " "error (connection closed) while connecting to " + m_url.get_host() + ':' + std::to_string(m_url.get_port())); case SSL_ERROR_WANT_CONNECT: // 7 throw sinsp_exception("Socket handler (" + m_id + "), " "error (the operation failed while attempting to connect " "the transport) while connecting to " + m_url.get_host() + ':' + std::to_string(m_url.get_port())); case SSL_ERROR_WANT_ACCEPT: // 8 throw sinsp_exception("Socket handler (" + m_id + "), " "error (the operation failed while attempting to accept a" " connection from the transport) while connecting to " + m_url.get_host() + ':' + std::to_string(m_url.get_port())); } } } else { throw sinsp_exception("Socket handler (" + m_id + "): " + m_url.to_string(false) + " SSL connection is null (" + strerror(errno) + ')'); } } g_logger.log("Socket handler (" + m_id + "): Connected: socket=" + std::to_string(m_socket) + ", collecting data from " + m_url.to_string(false) + m_path, sinsp_logger::SEV_DEBUG); if(m_url.is_secure() && m_ssl && m_ssl->verify_peer()) { if(SSL_get_peer_certificate(m_ssl_connection)) { long err = SSL_get_verify_result(m_ssl_connection); if(err != X509_V_OK && err != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && err != X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) { throw sinsp_exception("Socket handler (" + m_id + "): " + m_url.to_string(false) + " server certificate verification failed."); } } } m_connection_error = false; m_connecting = false; m_connected = true; } return true; } bool is_resolved() const { return (!m_address.empty() && m_sa && m_sa_len) || m_url.is_file(); } bool try_resolve() { if(is_resolved()) { return true; } else { if(inet_aton(m_url.get_host().c_str(), &m_serv_addr.sin_addr)) // IP address provided { m_address = m_url.get_host(); } else // name provided, resolve to IP address { m_serv_addr = {0}; int ret = 0; if(!m_dns_reqs) // first call, call async resolver { g_logger.log("Socket handler (" + m_id + ") resolving " + m_url.get_host(), sinsp_logger::SEV_TRACE); m_dns_reqs = make_gaicb(m_url.get_host()); ret = getaddrinfo_a(GAI_NOWAIT, m_dns_reqs.get(), 1, NULL); if(ret) { throw sinsp_exception("Socket handler (" + m_id + "): " + m_url.get_host() + " getaddrinfo_a() failed: " + gai_strerror(ret)); } return false; } else // rest of the calls, try to get resolver result { g_logger.log("Socket handler (" + m_id + ") checking resolve for " + m_url.get_host(), sinsp_logger::SEV_TRACE); ret = gai_error(m_dns_reqs[0]); g_logger.log("Socket handler (" + m_id + ") gai_error=" + std::to_string(ret), sinsp_logger::SEV_TRACE); if(!ret) { if(m_dns_reqs && m_dns_reqs[0] && m_dns_reqs[0]->ar_result) { for (struct addrinfo* ai = m_dns_reqs[0]->ar_result; ai; ai = ai->ai_next) { if(ai->ai_addrlen && ai->ai_addr && ai->ai_addr->sa_family == AF_INET) { struct sockaddr_in* saddr = (struct sockaddr_in*)ai->ai_addr; if(saddr->sin_addr.s_addr) { m_serv_addr.sin_addr.s_addr = saddr->sin_addr.s_addr; m_address = inet_ntoa(saddr->sin_addr); g_logger.log("Socket handler (" + m_id + "): " + m_url.get_host() + " resolved to " + m_address, sinsp_logger::SEV_TRACE); dns_cleanup(); break; } } } if(!m_serv_addr.sin_addr.s_addr) { g_logger.log("Socket handler (" + m_id + "): " + m_url.get_host() + " address not resolved yet.", sinsp_logger::SEV_TRACE); return false; } } else { throw sinsp_exception("Socket handler (" + m_id + "): " + m_url.get_host() + ", resolver request is NULL."); } } else { switch(ret) { case EAI_AGAIN: case EAI_INPROGRESS: g_logger.log("Socket handler (" + m_id + ") [" + m_url.get_host() + "]: " + gai_strerror(ret), sinsp_logger::SEV_DEBUG); break; case EAI_SYSTEM: g_logger.log("Socket handler (" + m_id + ") [" + m_url.get_host() + "]: " + ", resolver error: " + gai_strerror(ret) + ", system error: " + strerror(errno), sinsp_logger::SEV_ERROR); break; default: g_logger.log("Socket handler (" + m_id + ") [" + m_url.get_host() + "]: " + ", resolver error: " + gai_strerror(ret), sinsp_logger::SEV_ERROR); } return false; } } } } m_serv_addr.sin_family = AF_INET; m_serv_addr.sin_port = htons(m_url.get_port()); m_sa = (sockaddr*)&m_serv_addr; m_sa_len = sizeof(struct sockaddr_in); return true; } void connect_socket() { if(!m_sa || !m_sa_len) { if(m_url.is_file()) { if(m_url.get_path().length() > sizeof(m_file_addr.sun_path) - 1) { throw sinsp_exception("Invalid address (too long): [" + m_url.get_path() + ']'); } m_file_addr.sun_family = AF_UNIX; strncpy(m_file_addr.sun_path, m_url.get_path().c_str(), m_url.get_path().length()); m_file_addr.sun_path[sizeof(m_file_addr.sun_path) - 1]= '\0'; m_sa = (sockaddr*)&m_file_addr; m_sa_len = sizeof(struct sockaddr_un); } else if(m_url.is("https") || m_url.is("http")) { try_resolve(); } else { throw sinsp_exception("Socket handler (" + m_id + "): " + m_url.get_scheme() + " protocol not supported."); } } try_connect(); } std::string get_local_address() { struct sockaddr_in local_address; socklen_t address_length = sizeof(local_address); getsockname(m_socket, (struct sockaddr*)&local_address, &address_length); return std::string(inet_ntoa(local_address.sin_addr)); } int get_local_port() { struct sockaddr_in local_address; socklen_t address_length = sizeof(local_address); getsockname(m_socket, (struct sockaddr*)&local_address, &address_length); return (int) ntohs(local_address.sin_port); } void close_socket() { if(m_socket != -1) { g_logger.log("Socket handler (" + m_id + ") closing connection to " + m_url.to_string(false) + m_path, sinsp_logger::SEV_DEBUG); int ret = close(m_socket); if(ret < 0) { g_logger.log("Socket handler (" + m_id + ") connection [" + m_url.to_string(false) + m_path + "] error closing socket: " + strerror(errno), sinsp_logger::SEV_ERROR); } m_socket = -1; } m_enabled = false; m_connected = false; m_connecting = false; m_connect_called = true; } bool dns_req_done(struct gaicb** dns_reqs) const { if(dns_reqs && dns_reqs[0]) { int ret = gai_cancel(dns_reqs[0]); int err = gai_error(dns_reqs[0]); if(ret == EAI_ALLDONE || err == EAI_CANCELED) { return true; } else if(err == EAI_INPROGRESS || err == EAI_AGAIN) { std::string errstr = (err == EAI_INPROGRESS ) ? "processing in progress" : "resources temporarily unavailable"; g_logger.log("Socket handler (" + m_id + ") connection [" + m_url.to_string(false) + "], " " cancelling DNS request postponed (" + errstr + ")" "\n err: (" + std::to_string(err) + ") " + gai_strerror(err), sinsp_logger::SEV_DEBUG); return false; } else { g_logger.log("Socket handler (" + m_id + ") connection [" + m_url.to_string(false) + "], " "error canceling DNS request" "\n ret: (" + std::to_string(ret) + ") " + gai_strerror(ret) + "\n err: (" + std::to_string(err) + ") " + gai_strerror(err), sinsp_logger::SEV_ERROR); return false; } } return true; } void dns_cleanup() { for(dns_list_t::iterator it = m_pending_dns_reqs.begin(); it != m_pending_dns_reqs.end();) { if(dns_req_done(it->get())) { it = m_pending_dns_reqs.erase(it); g_logger.log("Socket handler: postponed canceling of DNS request succeeded, number of pending " "cancellation requests: " + std::to_string(m_pending_dns_reqs.size()), sinsp_logger::SEV_TRACE); } else { ++it; } } std::size_t pending_reqs = m_pending_dns_reqs.size(); if(pending_reqs) { g_logger.log("Socket handler: number of pending DNS cancellation requests is " + std::to_string(pending_reqs), (pending_reqs > 10) ? sinsp_logger::SEV_WARNING : sinsp_logger::SEV_TRACE); } if(!dns_req_done(m_dns_reqs.get())) { m_pending_dns_reqs.emplace_back(std::move(m_dns_reqs)); } m_dns_reqs = nullptr; } void ssl_cleanup() { SSL_free(m_ssl_connection); m_ssl_connection = 0; SSL_CTX_free(m_ssl_context); m_ssl_context = 0; } void cleanup() { free(m_http_parser); m_http_parser = nullptr; close_socket(); dns_cleanup(); ssl_cleanup(); } struct http_parser_data { std::string* m_data_buf = nullptr; std::vector* m_json = nullptr; int* m_http_response = nullptr; bool* m_msg_completed = nullptr; bool* m_fetching_state = nullptr; }; static int http_body_callback(http_parser* parser, const char* data, size_t len) { if(parser) { if(parser->data) { if(data && len) { http_parser_data* parser_data = (http_parser_data*) parser->data; if(parser_data->m_data_buf && parser_data->m_json) { parser_data->m_data_buf->append(data, len); // only try to parse this JSON if we are certain it is not pretty-printed // since this logic relies on JSONs in the stream being delimited by newlines // (and having no newlines themselves), pretty-printed JSONs can not be // handled here, but must be handled in the http_msg_completed_callback() if(parser_data->m_fetching_state) { if(!*(parser_data->m_fetching_state)) { std::string::size_type pos = parser_data->m_data_buf->find('\n'); while(pos != std::string::npos) { parser_data->m_json->push_back(parser_data->m_data_buf->substr(0, pos)); parser_data->m_data_buf->erase(0, pos + 1); pos = parser_data->m_data_buf->find('\n'); } } /*else { if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log("Socket handler (http_body_callback) data received, will be parsed on response end:" + *parser_data->m_data_buf, sinsp_logger::SEV_TRACE); } }*/ } } else { throw sinsp_exception("Socket handler (http_body_callback): http or json buffer is null."); } } } else { throw sinsp_exception("Socket handler (http_body_callback) parser data is null."); } } else { throw sinsp_exception("Socket handler (http_body_callback): parser is null."); } return 0; } static int http_msg_completed_callback(http_parser* parser) { if(parser && parser->data) { http_parser_data* parser_data = (http_parser_data*) parser->data; if(parser_data->m_fetching_state) { if(*(parser_data->m_fetching_state)) { std::string* buf = parser_data->m_data_buf; if(buf) { std::string::size_type pos = buf->rfind('\n'); if(pos != std::string::npos) { buf->erase(std::remove_if(buf->begin(), buf->end(), [](char c){return c == '\n' || c == '\r';}), buf->end()); parser_data->m_json->emplace_back(std::move(*buf)); buf->clear(); } else { g_logger.log("Initial state fetch completed, but no newline found!", sinsp_logger::SEV_ERROR); } *(parser_data->m_fetching_state) = false; } else { throw sinsp_exception("Socket handler (http_msg_completed_callback): parser data m_data_buf is null."); } } } else { throw sinsp_exception("Socket handler (http_msg_completed_callback): parser data m_data_buf is null."); } if(parser_data->m_msg_completed) { *(parser_data->m_msg_completed) = true; } else { throw sinsp_exception("Socket handler (http_msg_completed_callback): parser data m_msg_completed is null."); } if(parser_data->m_http_response) { *(parser_data->m_http_response) = parser->status_code; } else { throw sinsp_exception("Socket handler (http_msg_completed_callback): parser data m_http_response is null."); } } else { throw sinsp_exception("Socket handler (http_msg_completed_callback): parser or data null."); } return 0; } void init_http_parser() { m_msg_completed = false; m_http_response = -1; http_parser_settings_init(&m_http_parser_settings); m_http_parser_settings.on_body = http_body_callback; m_http_parser_settings.on_message_complete = http_msg_completed_callback; if(!m_http_parser) { m_http_parser = (http_parser *)std::malloc(sizeof(http_parser)); } m_http_parser_data.m_data_buf = &m_data_buf; m_http_parser_data.m_json = &m_json; m_http_parser_data.m_http_response = &m_http_response; m_http_parser_data.m_msg_completed = &m_msg_completed; m_http_parser_data.m_fetching_state = &m_fetching_state; http_parser_init(m_http_parser, HTTP_RESPONSE); m_http_parser->data = &m_http_parser_data; } static std::string get_http_reason(int status) { return http_reason::get(status); } using gaicb_t = std::unique_ptr; using dns_list_t = std::deque; gaicb_t make_gaicb(const std::string &host) { gaicb_t dns_reqs((struct gaicb**)calloc(1, sizeof(struct gaicb*))); dns_reqs[0] = (struct gaicb*)calloc(1, sizeof(struct gaicb)); dns_reqs[0]->ar_name = strdup(m_url.get_host().c_str()); return dns_reqs; } T& m_obj; std::string m_id; uri m_url; std::string m_keep_alive; std::string m_path; std::string m_address; bool m_connecting = false; bool m_connected = false; bool m_connect_called = false; bool m_connection_error = false; bool m_enabled = false; int m_socket = -1; bool m_blocking = false; std::vector m_buf; int m_sock_err = 0; gaicb_t m_dns_reqs = nullptr; static dns_list_t m_pending_dns_reqs; ssl_ptr_t m_ssl; bt_ptr_t m_bt; long m_timeout_ms; json_callback_func_t m_json_callback = nullptr; std::string m_data_buf; std::string m_request; std::string m_http_version; std::vector m_json_filters; std::vector m_json; json_query m_jq; bool m_ssl_init_complete = false; SSL_CTX* m_ssl_context = nullptr; SSL* m_ssl_connection = nullptr; password_vec_t m_ssl_key_pass; struct sockaddr_un m_file_addr = {0}; struct sockaddr_in m_serv_addr = {0}; struct sockaddr* m_sa = 0; socklen_t m_sa_len = 0; bool m_close_on_chunked_end = true; bool m_wants_send = false; int m_http_response = -1; bool m_msg_completed = false; http_parser_settings m_http_parser_settings; http_parser* m_http_parser = nullptr; http_parser_data m_http_parser_data; unsigned m_data_limit = 524288; // bytes // older versions of kubernetes send pretty-printed JSON by default; // that creates a problem with JSON-newline-delimit-based detection logic, // which relies on JSON itself having no newlines; while there is a way to // prevent this for entities (eg. nodes, pods) URIs by specifying '?pretty=false', // some cluster-level URIs (eg. /api) do not honor this parameter; // // this flag is true by default and it remains true until the first state http // request for this handler is completed, at which point all newlines are purged // from the string and the purged buffer is posted for further processing bool m_fetching_state = true; }; template const std::string socket_data_handler::HTTP_VERSION_10 = "1.0"; template const std::string socket_data_handler::HTTP_VERSION_11 = "1.1"; template typename socket_data_handler::dns_list_t socket_data_handler::m_pending_dns_reqs; #endif // HAS_CAPTURE sysdig-0.26.4/userspace/libsinsp/stats.cpp000066400000000000000000000051231352731327100205710ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ //////////////////////////////////////////////////////////////////////////// // Public definitions for the scap library //////////////////////////////////////////////////////////////////////////// #include "sinsp.h" #include "sinsp_int.h" #ifdef GATHER_INTERNAL_STATS void sinsp_stats::clear() { m_n_seen_evts = 0; m_n_drops = 0; m_n_preemptions = 0; m_n_noncached_fd_lookups = 0; m_n_cached_fd_lookups = 0; m_n_failed_fd_lookups = 0; m_n_threads = 0; m_n_fds = 0; m_n_added_fds = 0; m_n_removed_fds = 0; m_n_stored_evts = 0; m_n_store_drops = 0; m_n_retrieved_evts = 0; m_n_retrieve_drops = 0; m_metrics_registry.clear_all_metrics(); } void sinsp_stats::emit(FILE* f) { m_output_target = f; fprintf(f, "evts seen by driver: %" PRIu64 "\n", m_n_seen_evts); fprintf(f, "drops: %" PRIu64 "\n", m_n_drops); fprintf(f, "preemptions: %" PRIu64 "\n", m_n_preemptions); fprintf(f, "fd lookups: %" PRIu64 "(%" PRIu64 " cached %" PRIu64 " noncached)\n", m_n_noncached_fd_lookups + m_n_cached_fd_lookups, m_n_cached_fd_lookups, m_n_noncached_fd_lookups); fprintf(f, "failed fd lookups: %" PRIu64 "\n", m_n_failed_fd_lookups); fprintf(f, "n. threads: %" PRIu64 "\n", m_n_threads); fprintf(f, "n. fds: %" PRIu64 "\n", m_n_fds); fprintf(f, "added fds: %" PRIu64 "\n", m_n_added_fds); fprintf(f, "removed fds: %" PRIu64 "\n", m_n_removed_fds); fprintf(f, "stored evts: %" PRIu64 "\n", m_n_stored_evts); fprintf(f, "store drops: %" PRIu64 "\n", m_n_store_drops); fprintf(f, "retrieved evts: %" PRIu64 "\n", m_n_retrieved_evts); fprintf(f, "retrieve drops: %" PRIu64 "\n", m_n_retrieve_drops); for(internal_metrics::registry::metric_map_iterator_t it = m_metrics_registry.get_metrics().begin(); it != m_metrics_registry.get_metrics().end(); it++) { fprintf(f, "%s: ", it->first.get_description().c_str()); it->second->process(*this); } } void sinsp_stats::process(internal_metrics::counter& metric) { fprintf(m_output_target, "%" PRIu64 "\n", metric.get_value()); } #endif // GATHER_INTERNAL_STATS sysdig-0.26.4/userspace/libsinsp/stats.h000066400000000000000000000030031352731327100202310ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include "internal_metrics.h" #ifdef GATHER_INTERNAL_STATS // // Processing stats class. // Keeps a bunch of counters with key library performance metrics. // class SINSP_PUBLIC sinsp_stats : public internal_metrics::processor { public: void clear(); void emit(FILE* f); internal_metrics::registry& get_metrics_registry() { return m_metrics_registry; } void process(internal_metrics::counter& metric); uint64_t m_n_seen_evts; uint64_t m_n_drops; uint64_t m_n_preemptions; uint64_t m_n_noncached_fd_lookups; uint64_t m_n_cached_fd_lookups; uint64_t m_n_failed_fd_lookups; uint64_t m_n_threads; uint64_t m_n_fds; uint64_t m_n_added_fds; uint64_t m_n_removed_fds; uint64_t m_n_stored_evts; uint64_t m_n_store_drops; uint64_t m_n_retrieved_evts; uint64_t m_n_retrieve_drops; private: internal_metrics::registry m_metrics_registry; FILE* m_output_target; }; #endif // GATHER_INTERNAL_STATS sysdig-0.26.4/userspace/libsinsp/stopwatch.cpp000066400000000000000000000013321352731327100214450ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // stopwatch.cpp // // stopwatch utility // #include "stopwatch.h" sinsp_stopwatch::sinsp_stopwatch() { start(); } sysdig-0.26.4/userspace/libsinsp/stopwatch.h000066400000000000000000000030101352731327100211050ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // stopwatch.h // // stopwatch utility // #pragma once #include class sinsp_stopwatch { public: sinsp_stopwatch(); void stop(); void start(); void reset(); template typename T::rep elapsed() const { return std::chrono::duration_cast(m_stop - m_start).count(); } private: void record(std::chrono::high_resolution_clock::time_point& tp); std::chrono::high_resolution_clock::time_point m_start; std::chrono::high_resolution_clock::time_point m_stop; }; inline void sinsp_stopwatch::sinsp_stopwatch::reset() { m_start = std::chrono::high_resolution_clock::time_point::min(); m_stop = m_start; } inline void sinsp_stopwatch::sinsp_stopwatch::start() { record(m_start); } inline void sinsp_stopwatch::sinsp_stopwatch::stop() { record(m_stop); } inline void sinsp_stopwatch::record(std::chrono::high_resolution_clock::time_point& tp) { tp = std::chrono::high_resolution_clock::now(); } sysdig-0.26.4/userspace/libsinsp/table.cpp000066400000000000000000000774311352731327100205350ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #ifndef _WIN32 #include #endif #include "sinsp.h" #include "sinsp_int.h" #include "../../driver/ppm_ringbuffer.h" #include "filter.h" #include "filterchecks.h" #include "table.h" extern sinsp_filter_check_list g_filterlist; extern sinsp_evttables g_infotables; // // // Table sorter functor typedef struct table_row_cmp { bool operator()(const sinsp_sample_row& src, const sinsp_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; }table_row_cmp; sinsp_table::sinsp_table(sinsp* inspector, tabletype type, uint64_t refresh_interval_ns, sinsp_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; } sinsp_table::~sinsp_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 sinsp_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 == sinsp_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(); } ////////////////////////////////////////////////////////////////////////////////////// // Extract the tokens ////////////////////////////////////////////////////////////////////////////////////// m_premerge_extractors.clear(); for(auto vit : *entries) { sinsp_filter_check* chk = g_filterlist.new_filter_check_from_fldname(vit.get_field(m_view_depth), m_inspector, false); if(chk == NULL) { throw sinsp_exception("invalid field name " + vit.get_field(m_view_depth)); } chk->m_aggregation = (sinsp_field_aggregation)vit.m_aggregation; m_chks_to_free.push_back(chk); 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); m_is_key_present = true; } else { m_premerge_extractors.push_back(chk); } } if(m_type == sinsp_table::TT_TABLE) { // // Make sure this is a valid table // if(!m_is_key_present) { throw sinsp_exception("table is missing the key"); } } else { sinsp_filter_check* chk = g_filterlist.new_filter_check_from_fldname("util.cnt", m_inspector, false); if(chk == NULL) { throw sinsp_exception("internal table error"); } chk->m_aggregation = A_NONE; m_chks_to_free.push_back(chk); 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); m_is_key_present = true; } m_premerge_fld_pointers = new sinsp_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)->get_field_info()->m_type); m_premerge_legend.push_back(*(*it)->get_field_info()); } m_premerge_vals_array_sz = (m_n_fields - 1) * sizeof(sinsp_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 != sinsp_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; } sinsp_filter_check* chk = m_premerge_extractors[j]; chk->m_merge_aggregation = (sinsp_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); m_groupby_columns.insert(m_groupby_columns.begin(), j); } else { m_postmerge_extractors.push_back(chk); m_groupby_columns.push_back(j); } } m_postmerge_fld_pointers = new sinsp_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)->get_field_info()->m_type); m_postmerge_legend.push_back(*(*it)->get_field_info()); } m_postmerge_vals_array_sz = (m_n_postmerge_fields - 1) * sizeof(sinsp_table_field); } void sinsp_table::add_row(bool merging) { uint32_t j; sinsp_table_field key(m_fld_pointers[0].m_val, m_fld_pointers[0].m_len, m_fld_pointers[0].m_cnt); if(m_type == sinsp_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 = (sinsp_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; } sinsp_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 = (sinsp_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 sinsp_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++) { uint32_t len; uint8_t* val = m_premerge_extractors[j]->extract(evt, &len); sinsp_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. // if(val == NULL) { 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 { pfld->m_val = val; pfld->m_len = get_field_len(j); pfld->m_val = m_buffer->copy(val, pfld->m_len); pfld->m_cnt = 1; } } // // Add the row // add_row(false); return; } void sinsp_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_SYSDIGEVENT_X; tscapevt.len = 0; tscapevt.nparams = 0; tevt.m_inspector = m_inspector; tevt.m_info = &(g_infotables.m_event_info[PPME_SYSDIGEVENT_X]); tevt.m_pevt = NULL; tevt.m_cpuid = 0; tevt.m_evtnum = 0; tevt.m_pevt = &tscapevt; tevt.m_fdinfo = NULL; threadtable->loop([&] (sinsp_threadinfo& tinfo) { tevt.m_tinfo = &tinfo; tscapevt.tid = tevt.m_tinfo->m_tid; if(m_filter) { if(!m_filter->run(&tevt)) { return true; } } process_event(&tevt); return true; }); } void sinsp_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 == sinsp_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 sinsp_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++) { sinsp_filter_check* 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), 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 sinsp_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++) { sinsp_filter_check* 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), 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 sinsp_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, 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 // sinsp_table_field* sinsp_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, 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 sinsp_table::sort_sample() { if(m_type == sinsp_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* sinsp_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 != sinsp_table::OT_CURSES) { #endif if(m_output_type == sinsp_table::OT_RAW) { print_raw(m_sample_data, time_delta); } else if(m_output_type == sinsp_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 sinsp_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 == sinsp_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 sinsp_table::get_sorting_col() { return (uint32_t)m_sorting_col + 1; } void sinsp_table::create_sample() { if(m_type == sinsp_table::TT_TABLE) { uint32_t j; m_full_sample_data.clear(); sinsp_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++) { sinsp_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(); sinsp_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 sinsp_table::add_fields_sum(ppm_param_type type, sinsp_table_field *dst, sinsp_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 sinsp_table::add_fields_sum_of_avg(ppm_param_type type, sinsp_table_field *dst, sinsp_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 sinsp_table::add_fields_max(ppm_param_type type, sinsp_table_field *dst, sinsp_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 sinsp_table::add_fields_min(ppm_param_type type, sinsp_table_field *dst, sinsp_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 sinsp_table::add_fields(uint32_t dst_id, sinsp_table_field* src, uint32_t aggr) { ppm_param_type type = (*m_types)[dst_id]; sinsp_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 sinsp_table::get_field_len(uint32_t id) { ppm_param_type type; sinsp_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_UINT8: case PT_SIGTYPE: return 1; case PT_FLAGS16: case PT_UINT16: case PT_PORT: case PT_SYSCALLID: return 2; case PT_UINT32: case PT_FLAGS32: 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: default: ASSERT(false); return false; } } uint8_t* sinsp_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 sinsp_table::switch_buffers() { if(m_buffer == &m_buffer1) { m_buffer = &m_buffer2; } else { m_buffer = &m_buffer1; } } pair sinsp_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])->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])->get_field_info(); ASSERT(res.first != NULL); m_printer->set_val(types->at(0), 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; } sinsp_table_field* sinsp_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 sinsp_table::get_row_from_key(sinsp_table_field* key) { uint32_t j; for(j = 0; j < m_sample_data->size(); j++) { sinsp_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 sinsp_table::set_paused(bool paused) { m_paused = paused; } void sinsp_table::clear() { if(m_type == sinsp_table::TT_LIST) { m_full_sample_data.clear(); m_buffer->clear(); } else { ASSERT(false); } } sysdig-0.26.4/userspace/libsinsp/table.h000066400000000000000000000177541352731327100202040ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #define SINSP_TABLE_DEFAULT_REFRESH_INTERVAL_NS 1000000000 #define SINSP_TABLE_BUFFER_ENTRY_SIZE 16384 class sinsp_filter_check_reference; typedef enum sysdig_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, }sysdig_table_action; class sinsp_table_field { public: sinsp_table_field() { m_val = NULL; } sinsp_table_field(uint8_t* val, uint32_t len, uint32_t cnt) { m_len = len; m_val = val; m_cnt = cnt; } bool operator==(const sinsp_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 sinsp_table_field_storage : public sinsp_table_field { public: sinsp_table_field_storage() { m_storage_len = STF_STORAGE_BUFSIZE; m_val = new uint8_t[m_storage_len]; m_isvalid = false; } ~sinsp_table_field_storage() { if(m_val != NULL) { delete[] m_val; } } void copy(sinsp_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 sinsp_table_field_hasher { size_t operator()(const sinsp_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 sinsp_table_buffer { public: sinsp_table_buffer() { push_buffer(); } ~sinsp_table_buffer() { for(auto it = m_bufs.begin(); it != m_bufs.end(); ++it) { delete[] *it; } } void push_buffer() { m_curbuf = new uint8_t[SINSP_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 >= SINSP_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 >= SINSP_TABLE_BUFFER_ENTRY_SIZE) { ASSERT(false); throw sinsp_exception("field value too long"); } if(m_pos + len >= SINSP_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; } vector m_bufs; uint8_t* m_curbuf; uint32_t m_pos; }; class sinsp_sample_row { public: sinsp_table_field m_key; vector m_values; }; class sinsp_table { public: enum tabletype { TT_NONE = 0, TT_TABLE, TT_LIST, }; enum output_type { OT_CURSES, OT_RAW, OT_JSON, }; sinsp_table(sinsp* inspector, tabletype type, uint64_t refresh_interval_ns, sinsp_table::output_type output_type, uint32_t json_first_row, uint32_t json_last_row); ~sinsp_table(); void configure(vector* entries, const 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 // sinsp_table_field* search_in_sample(string text); void sort_sample(); vector* get_sample(uint64_t time_delta); 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(); pair get_row_key_name_and_val(uint32_t rownum, bool force); sinsp_table_field* get_row_key(uint32_t rownum); int32_t get_row_from_key(sinsp_table_field* key); void set_paused(bool paused); void set_freetext_filter(string filter) { m_freetext_filter = filter; } tabletype get_type() { return m_type; } void set_refresh_interval(uint64_t newinterval_ns) { m_refresh_interval_ns = newinterval_ns; } void clear(); bool is_merging() { return m_do_merging; } bool is_sorting_ascending() { 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; 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, sinsp_table_field* dst, sinsp_table_field* src); inline void add_fields_sum_of_avg(ppm_param_type type, sinsp_table_field* dst, sinsp_table_field* src); inline void add_fields_max(ppm_param_type type, sinsp_table_field* dst, sinsp_table_field* src); inline void add_fields_min(ppm_param_type type, sinsp_table_field* dst, sinsp_table_field* src); inline void add_fields(uint32_t dst_id, sinsp_table_field* src, uint32_t aggr); void process_proctable(sinsp_evt* evt); inline uint32_t get_field_len(uint32_t id); inline uint8_t* get_default_val(filtercheck_field_info* fld); void create_sample(); void switch_buffers(); void print_raw(vector* sample_data, uint64_t time_delta); void print_json(vector* sample_data, uint64_t time_delta); sinsp* m_inspector; unordered_map* m_table; unordered_map m_premerge_table; unordered_map m_merge_table; vector m_premerge_legend; vector m_premerge_extractors; vector m_postmerge_extractors; vector* m_extractors; vector m_chks_to_free; vector m_premerge_types; vector m_postmerge_types; bool m_is_key_present; bool m_is_groupby_key_present; vector m_groupby_columns; vector m_postmerge_legend; sinsp_table_field* m_fld_pointers; sinsp_table_field* m_premerge_fld_pointers; sinsp_table_field* m_postmerge_fld_pointers; uint32_t m_n_fields; uint32_t m_n_premerge_fields; uint32_t m_n_postmerge_fields; sinsp_table_buffer* m_buffer; sinsp_table_buffer m_buffer1; sinsp_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; vector m_full_sample_data; vector m_filtered_sample_data; vector* m_sample_data; sinsp_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; 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; }; sysdig-0.26.4/userspace/libsinsp/third-party/000077500000000000000000000000001352731327100211755ustar00rootroot00000000000000sysdig-0.26.4/userspace/libsinsp/third-party/jsoncpp/000077500000000000000000000000001352731327100226515ustar00rootroot00000000000000sysdig-0.26.4/userspace/libsinsp/third-party/jsoncpp/json/000077500000000000000000000000001352731327100236225ustar00rootroot00000000000000sysdig-0.26.4/userspace/libsinsp/third-party/jsoncpp/json/json-forwards.h000066400000000000000000000214031352731327100265710ustar00rootroot00000000000000/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/). /// It is intended to be used with #include "json/json-forwards.h" /// This header provides forward declaration for all JsonCpp types. // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: LICENSE // ////////////////////////////////////////////////////////////////////// /* The JsonCpp library's source code, including accompanying documentation, tests and demonstration applications, are licensed under the following conditions... The author (Baptiste Lepilleur) explicitly disclaims copyright in all jurisdictions which recognize such a disclaimer. In such jurisdictions, this software is released into the Public Domain. In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is released under the terms of the MIT License (see below). In jurisdictions which recognize Public Domain property, the user of this software may choose to accept it either as 1) Public Domain, 2) under the conditions of the MIT License (see below), or 3) under the terms of dual Public Domain/MIT License conditions described here, as they choose. The MIT License is about as close to Public Domain as a license can get, and is described in clear, concise terms at: http://en.wikipedia.org/wiki/MIT_License The full text of the MIT License follows: ======================================================================== Copyright (c) 2007-2010 Baptiste Lepilleur Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ======================================================================== (END LICENSE TEXT) The MIT license is compatible with both the GPL and commercial software, affording one all of the rights of Public Domain with the minor nuisance of being required to keep the above copyright notice and license text in the source code. Note also that by accepting the Public Domain "license" you can re-license your copy using whatever license you like. */ // ////////////////////////////////////////////////////////////////////// // End of content of file: LICENSE // ////////////////////////////////////////////////////////////////////// #ifndef JSON_FORWARD_AMALGATED_H_INCLUDED # define JSON_FORWARD_AMALGATED_H_INCLUDED /// If defined, indicates that the source file is amalgated /// to prevent private header inclusion. #define JSON_IS_AMALGAMATION // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: include/json/config.h // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef JSON_CONFIG_H_INCLUDED #define JSON_CONFIG_H_INCLUDED /// If defined, indicates that json library is embedded in CppTL library. //# define JSON_IN_CPPTL 1 /// If defined, indicates that json may leverage CppTL library //# define JSON_USE_CPPTL 1 /// If defined, indicates that cpptl vector based map should be used instead of /// std::map /// as Value container. //# define JSON_USE_CPPTL_SMALLMAP 1 // If non-zero, the library uses exceptions to report bad input instead of C // assertion macros. The default is to use exceptions. #ifndef JSON_USE_EXCEPTION #define JSON_USE_EXCEPTION 1 #endif /// If defined, indicates that the source file is amalgated /// to prevent private header inclusion. /// Remarks: it is automatically defined in the generated amalgated header. // #define JSON_IS_AMALGAMATION #ifdef JSON_IN_CPPTL #include #ifndef JSON_USE_CPPTL #define JSON_USE_CPPTL 1 #endif #endif #ifdef JSON_IN_CPPTL #define JSON_API CPPTL_API #elif defined(JSON_DLL_BUILD) #if defined(_MSC_VER) #define JSON_API __declspec(dllexport) #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING #endif // if defined(_MSC_VER) #elif defined(JSON_DLL) #if defined(_MSC_VER) #define JSON_API __declspec(dllimport) #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING #endif // if defined(_MSC_VER) #endif // ifdef JSON_IN_CPPTL #if !defined(JSON_API) #define JSON_API #endif // If JSON_NO_INT64 is defined, then Json only support C++ "int" type for // integer // Storages, and 64 bits integer support is disabled. // #define JSON_NO_INT64 1 #if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 // Microsoft Visual Studio 6 only support conversion from __int64 to double // (no conversion from unsigned __int64). #define JSON_USE_INT64_DOUBLE_CONVERSION 1 // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' // characters in the debug information) // All projects I've ever seen with VS6 were using this globally (not bothering // with pragma push/pop). #pragma warning(disable : 4786) #endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 #if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 /// Indicates that the following function is deprecated. #define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) #elif defined(__clang__) && defined(__has_feature) #if __has_feature(attribute_deprecated_with_message) #define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) #endif #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) #define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) #elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) #define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) #endif #if !defined(JSONCPP_DEPRECATED) #define JSONCPP_DEPRECATED(message) #endif // if !defined(JSONCPP_DEPRECATED) namespace Json { typedef int Int; typedef unsigned int UInt; #if defined(JSON_NO_INT64) typedef int LargestInt; typedef unsigned int LargestUInt; #undef JSON_HAS_INT64 #else // if defined(JSON_NO_INT64) // For Microsoft Visual use specific types as long long is not supported #if defined(_MSC_VER) // Microsoft Visual Studio typedef __int64 Int64; typedef unsigned __int64 UInt64; #else // if defined(_MSC_VER) // Other platforms, use long long typedef long long int Int64; typedef unsigned long long int UInt64; #endif // if defined(_MSC_VER) typedef Int64 LargestInt; typedef UInt64 LargestUInt; #define JSON_HAS_INT64 #endif // if defined(JSON_NO_INT64) } // end namespace Json #endif // JSON_CONFIG_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/config.h // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: include/json/forwards.h // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef JSON_FORWARDS_H_INCLUDED #define JSON_FORWARDS_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) #include "config.h" #endif // if !defined(JSON_IS_AMALGAMATION) namespace Json { // writer.h class FastWriter; class StyledWriter; // reader.h class Reader; // features.h class Features; // value.h typedef unsigned int ArrayIndex; class StaticString; class Path; class PathArgument; class Value; class ValueIteratorBase; class ValueIterator; class ValueConstIterator; } // namespace Json #endif // JSON_FORWARDS_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/forwards.h // ////////////////////////////////////////////////////////////////////// #endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED sysdig-0.26.4/userspace/libsinsp/third-party/jsoncpp/json/json.h000066400000000000000000001754061352731327100247610ustar00rootroot00000000000000/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/). /// It is intended to be used with #include "json/json.h" // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: LICENSE // ////////////////////////////////////////////////////////////////////// /* The JsonCpp library's source code, including accompanying documentation, tests and demonstration applications, are licensed under the following conditions... The author (Baptiste Lepilleur) explicitly disclaims copyright in all jurisdictions which recognize such a disclaimer. In such jurisdictions, this software is released into the Public Domain. In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is released under the terms of the MIT License (see below). In jurisdictions which recognize Public Domain property, the user of this software may choose to accept it either as 1) Public Domain, 2) under the conditions of the MIT License (see below), or 3) under the terms of dual Public Domain/MIT License conditions described here, as they choose. The MIT License is about as close to Public Domain as a license can get, and is described in clear, concise terms at: http://en.wikipedia.org/wiki/MIT_License The full text of the MIT License follows: ======================================================================== Copyright (c) 2007-2010 Baptiste Lepilleur Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ======================================================================== (END LICENSE TEXT) The MIT license is compatible with both the GPL and commercial software, affording one all of the rights of Public Domain with the minor nuisance of being required to keep the above copyright notice and license text in the source code. Note also that by accepting the Public Domain "license" you can re-license your copy using whatever license you like. */ // ////////////////////////////////////////////////////////////////////// // End of content of file: LICENSE // ////////////////////////////////////////////////////////////////////// #ifndef JSON_AMALGATED_H_INCLUDED # define JSON_AMALGATED_H_INCLUDED /// If defined, indicates that the source file is amalgated /// to prevent private header inclusion. #define JSON_IS_AMALGAMATION // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: include/json/version.h // ////////////////////////////////////////////////////////////////////// // DO NOT EDIT. This file (and "version") is generated by CMake. // Run CMake configure step to update it. #ifndef JSON_VERSION_H_INCLUDED # define JSON_VERSION_H_INCLUDED # define JSONCPP_VERSION_STRING "0.10.6" # define JSONCPP_VERSION_MAJOR 0 # define JSONCPP_VERSION_MINOR 10 # define JSONCPP_VERSION_PATCH 6 # define JSONCPP_VERSION_QUALIFIER # define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) #endif // JSON_VERSION_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/version.h // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: include/json/config.h // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef JSON_CONFIG_H_INCLUDED #define JSON_CONFIG_H_INCLUDED /// If defined, indicates that json library is embedded in CppTL library. //# define JSON_IN_CPPTL 1 /// If defined, indicates that json may leverage CppTL library //# define JSON_USE_CPPTL 1 /// If defined, indicates that cpptl vector based map should be used instead of /// std::map /// as Value container. //# define JSON_USE_CPPTL_SMALLMAP 1 // If non-zero, the library uses exceptions to report bad input instead of C // assertion macros. The default is to use exceptions. #ifndef JSON_USE_EXCEPTION #define JSON_USE_EXCEPTION 1 #endif /// If defined, indicates that the source file is amalgated /// to prevent private header inclusion. /// Remarks: it is automatically defined in the generated amalgated header. // #define JSON_IS_AMALGAMATION #ifdef JSON_IN_CPPTL #include #ifndef JSON_USE_CPPTL #define JSON_USE_CPPTL 1 #endif #endif #ifdef JSON_IN_CPPTL #define JSON_API CPPTL_API #elif defined(JSON_DLL_BUILD) #if defined(_MSC_VER) #define JSON_API __declspec(dllexport) #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING #endif // if defined(_MSC_VER) #elif defined(JSON_DLL) #if defined(_MSC_VER) #define JSON_API __declspec(dllimport) #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING #endif // if defined(_MSC_VER) #endif // ifdef JSON_IN_CPPTL #if !defined(JSON_API) #define JSON_API #endif // If JSON_NO_INT64 is defined, then Json only support C++ "int" type for // integer // Storages, and 64 bits integer support is disabled. // #define JSON_NO_INT64 1 #if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 // Microsoft Visual Studio 6 only support conversion from __int64 to double // (no conversion from unsigned __int64). #define JSON_USE_INT64_DOUBLE_CONVERSION 1 // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' // characters in the debug information) // All projects I've ever seen with VS6 were using this globally (not bothering // with pragma push/pop). #pragma warning(disable : 4786) #endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 #if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 /// Indicates that the following function is deprecated. #define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) #elif defined(__clang__) && defined(__has_feature) #if __has_feature(attribute_deprecated_with_message) #define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) #endif #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) #define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) #elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) #define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) #endif #if !defined(JSONCPP_DEPRECATED) #define JSONCPP_DEPRECATED(message) #endif // if !defined(JSONCPP_DEPRECATED) namespace Json { typedef int Int; typedef unsigned int UInt; #if defined(JSON_NO_INT64) typedef int LargestInt; typedef unsigned int LargestUInt; #undef JSON_HAS_INT64 #else // if defined(JSON_NO_INT64) // For Microsoft Visual use specific types as long long is not supported #if defined(_MSC_VER) // Microsoft Visual Studio typedef __int64 Int64; typedef unsigned __int64 UInt64; #else // if defined(_MSC_VER) // Other platforms, use long long typedef long long int Int64; typedef unsigned long long int UInt64; #endif // if defined(_MSC_VER) typedef Int64 LargestInt; typedef UInt64 LargestUInt; #define JSON_HAS_INT64 #endif // if defined(JSON_NO_INT64) } // end namespace Json #endif // JSON_CONFIG_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/config.h // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: include/json/forwards.h // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef JSON_FORWARDS_H_INCLUDED #define JSON_FORWARDS_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) #include "config.h" #endif // if !defined(JSON_IS_AMALGAMATION) namespace Json { // writer.h class FastWriter; class StyledWriter; // reader.h class Reader; // features.h class Features; // value.h typedef unsigned int ArrayIndex; class StaticString; class Path; class PathArgument; class Value; class ValueIteratorBase; class ValueIterator; class ValueConstIterator; } // namespace Json #endif // JSON_FORWARDS_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/forwards.h // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: include/json/features.h // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef CPPTL_JSON_FEATURES_H_INCLUDED #define CPPTL_JSON_FEATURES_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) #include "forwards.h" #endif // if !defined(JSON_IS_AMALGAMATION) namespace Json { /** \brief Configuration passed to reader and writer. * This configuration object can be used to force the Reader or Writer * to behave in a standard conforming way. */ class JSON_API Features { public: /** \brief A configuration that allows all features and assumes all strings * are UTF-8. * - C & C++ comments are allowed * - Root object can be any JSON value * - Assumes Value strings are encoded in UTF-8 */ static Features all(); /** \brief A configuration that is strictly compatible with the JSON * specification. * - Comments are forbidden. * - Root object must be either an array or an object value. * - Assumes Value strings are encoded in UTF-8 */ static Features strictMode(); /** \brief Initialize the configuration like JsonConfig::allFeatures; */ Features(); /// \c true if comments are allowed. Default: \c true. bool allowComments_; /// \c true if root must be either an array or an object value. Default: \c /// false. bool strictRoot_; }; } // namespace Json #endif // CPPTL_JSON_FEATURES_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/features.h // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: include/json/value.h // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef CPPTL_JSON_H_INCLUDED #define CPPTL_JSON_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) #include "forwards.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include #include #ifndef JSON_USE_CPPTL_SMALLMAP #include #else #include #endif #ifdef JSON_USE_CPPTL #include #endif // Disable warning C4251: : needs to have dll-interface to // be used by... #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #pragma warning(push) #pragma warning(disable : 4251) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) //Conditional NORETURN attribute on the throw functions would: // a) suppress false positives from static code analysis // b) possibly improve optimization opportunities. #if !defined(JSONCPP_NORETURN) # if defined(_MSC_VER) # define JSONCPP_NORETURN __declspec(noreturn) # elif defined(__GNUC__) # define JSONCPP_NORETURN __attribute__ ((__noreturn__)) # else # define JSONCPP_NORETURN # endif #endif /** \brief JSON (JavaScript Object Notation). */ namespace Json { /** Base class for all exceptions we throw. * * We use nothing but these internally. Of course, STL can throw others. */ class JSON_API Exception : public std::exception { public: Exception(std::string const& msg); virtual ~Exception() throw(); virtual char const* what() const throw(); protected: std::string const msg_; }; /** Exceptions which the user cannot easily avoid. * * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input * * \remark derived from Json::Exception */ class JSON_API RuntimeError : public Exception { public: RuntimeError(std::string const& msg); }; /** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. * * These are precondition-violations (user bugs) and internal errors (our bugs). * * \remark derived from Json::Exception */ class JSON_API LogicError : public Exception { public: LogicError(std::string const& msg); }; /// used internally JSONCPP_NORETURN void throwRuntimeError(std::string const& msg); /// used internally JSONCPP_NORETURN void throwLogicError(std::string const& msg); /** \brief Type of the value held by a Value object. */ enum ValueType { nullValue = 0, ///< 'null' value intValue, ///< signed integer value uintValue, ///< unsigned integer value realValue, ///< double value stringValue, ///< UTF-8 string value booleanValue, ///< bool value arrayValue, ///< array value (ordered list) objectValue ///< object value (collection of name/value pairs). }; enum CommentPlacement { commentBefore = 0, ///< a comment placed on the line before a value commentAfterOnSameLine, ///< a comment just after a value on the same line commentAfter, ///< a comment on the line after a value (only make sense for /// root value) numberOfCommentPlacement }; //# ifdef JSON_USE_CPPTL // typedef CppTL::AnyEnumerator EnumMemberNames; // typedef CppTL::AnyEnumerator EnumValues; //# endif /** \brief Lightweight wrapper to tag static string. * * Value constructor and objectValue member assignement takes advantage of the * StaticString and avoid the cost of string duplication when storing the * string or the member name. * * Example of usage: * \code * Json::Value aValue( StaticString("some text") ); * Json::Value object; * static const StaticString code("code"); * object[code] = 1234; * \endcode */ class JSON_API StaticString { public: explicit StaticString(const char* czstring) : c_str_(czstring) {} operator const char*() const { return c_str_; } const char* c_str() const { return c_str_; } private: const char* c_str_; }; /** \brief Represents a JSON value. * * This class is a discriminated union wrapper that can represents a: * - signed integer [range: Value::minInt - Value::maxInt] * - unsigned integer (range: 0 - Value::maxUInt) * - double * - UTF-8 string * - boolean * - 'null' * - an ordered list of Value * - collection of name/value pairs (javascript object) * * The type of the held value is represented by a #ValueType and * can be obtained using type(). * * Values of an #objectValue or #arrayValue can be accessed using operator[]() * methods. * Non-const methods will automatically create the a #nullValue element * if it does not exist. * The sequence of an #arrayValue will be automatically resized and initialized * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. * * The get() methods can be used to obtain default value in the case the * required element does not exist. * * It is possible to iterate over the list of a #objectValue values using * the getMemberNames() method. * * \note #Value string-length fit in size_t, but keys must be < 2^30. * (The reason is an implementation detail.) A #CharReader will raise an * exception if a bound is exceeded to avoid security holes in your app, * but the Value API does *not* check bounds. That is the responsibility * of the caller. */ class JSON_API Value { friend class ValueIteratorBase; public: typedef std::vector Members; typedef ValueIterator iterator; typedef ValueConstIterator const_iterator; typedef Json::UInt UInt; typedef Json::Int Int; #if defined(JSON_HAS_INT64) typedef Json::UInt64 UInt64; typedef Json::Int64 Int64; #endif // defined(JSON_HAS_INT64) typedef Json::LargestInt LargestInt; typedef Json::LargestUInt LargestUInt; typedef Json::ArrayIndex ArrayIndex; static const Value& nullRef; #if !defined(__ARMEL__) /// \deprecated This exists for binary compatibility only. Use nullRef. static const Value null; #endif /// Minimum signed integer value that can be stored in a Json::Value. static const LargestInt minLargestInt; /// Maximum signed integer value that can be stored in a Json::Value. static const LargestInt maxLargestInt; /// Maximum unsigned integer value that can be stored in a Json::Value. static const LargestUInt maxLargestUInt; /// Minimum signed int value that can be stored in a Json::Value. static const Int minInt; /// Maximum signed int value that can be stored in a Json::Value. static const Int maxInt; /// Maximum unsigned int value that can be stored in a Json::Value. static const UInt maxUInt; #if defined(JSON_HAS_INT64) /// Minimum signed 64 bits int value that can be stored in a Json::Value. static const Int64 minInt64; /// Maximum signed 64 bits int value that can be stored in a Json::Value. static const Int64 maxInt64; /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. static const UInt64 maxUInt64; #endif // defined(JSON_HAS_INT64) private: #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION class CZString { public: enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy }; CZString(ArrayIndex index); CZString(char const* str, unsigned length, DuplicationPolicy allocate); CZString(CZString const& other); ~CZString(); CZString& operator=(CZString other); bool operator<(CZString const& other) const; bool operator==(CZString const& other) const; ArrayIndex index() const; //const char* c_str() const; ///< \deprecated char const* data() const; unsigned length() const; bool isStaticString() const; private: void swap(CZString& other); struct StringStorage { unsigned policy_: 2; unsigned length_: 30; // 1GB max }; char const* cstr_; // actually, a prefixed string, unless policy is noDup union { ArrayIndex index_; StringStorage storage_; }; }; public: #ifndef JSON_USE_CPPTL_SMALLMAP typedef std::map ObjectValues; #else typedef CppTL::SmallMap ObjectValues; #endif // ifndef JSON_USE_CPPTL_SMALLMAP #endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION public: /** \brief Create a default Value of the given type. This is a very useful constructor. To create an empty array, pass arrayValue. To create an empty object, pass objectValue. Another Value can then be set to this one by assignment. This is useful since clear() and resize() will not alter types. Examples: \code Json::Value null_value; // null Json::Value arr_value(Json::arrayValue); // [] Json::Value obj_value(Json::objectValue); // {} \endcode */ Value(ValueType type = nullValue); Value(Int value); Value(UInt value); #if defined(JSON_HAS_INT64) Value(Int64 value); Value(UInt64 value); #endif // if defined(JSON_HAS_INT64) Value(double value); Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) Value(const char* begin, const char* end); ///< Copy all, incl zeroes. /** \brief Constructs a value from a static string. * Like other value string constructor but do not duplicate the string for * internal storage. The given string must remain alive after the call to this * constructor. * \note This works only for null-terminated strings. (We cannot change the * size of this class, so we have nowhere to store the length, * which might be computed later for various operations.) * * Example of usage: * \code * static StaticString foo("some text"); * Json::Value aValue(foo); * \endcode */ Value(const StaticString& value); Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too. #ifdef JSON_USE_CPPTL Value(const CppTL::ConstString& value); #endif Value(bool value); /// Deep copy. Value(const Value& other); ~Value(); /// Deep copy, then swap(other). /// \note Over-write existing comments. To preserve comments, use #swapPayload(). Value &operator=(const Value &other); /// Swap everything. void swap(Value& other); /// Swap values but leave comments and source offsets in place. void swapPayload(Value& other); ValueType type() const; /// Compare payload only, not comments etc. bool operator<(const Value& other) const; bool operator<=(const Value& other) const; bool operator>=(const Value& other) const; bool operator>(const Value& other) const; bool operator==(const Value& other) const; bool operator!=(const Value& other) const; int compare(const Value& other) const; const char* asCString() const; ///< Embedded zeroes could cause you trouble! std::string asString() const; ///< Embedded zeroes are possible. /** Get raw char* of string-value. * \return false if !string. (Seg-fault if str or end are NULL.) */ bool getString( char const** begin, char const** end) const; #ifdef JSON_USE_CPPTL CppTL::ConstString asConstString() const; #endif Int asInt() const; UInt asUInt() const; #if defined(JSON_HAS_INT64) Int64 asInt64() const; UInt64 asUInt64() const; #endif // if defined(JSON_HAS_INT64) LargestInt asLargestInt() const; LargestUInt asLargestUInt() const; float asFloat() const; double asDouble() const; bool asBool() const; bool isNull() const; bool isBool() const; bool isInt() const; bool isInt64() const; bool isUInt() const; bool isUInt64() const; bool isIntegral() const; bool isDouble() const; bool isNumeric() const; bool isString() const; bool isArray() const; bool isObject() const; bool isConvertibleTo(ValueType other) const; /// Number of values in array or object ArrayIndex size() const; /// \brief Return true if empty array, empty object, or null; /// otherwise, false. bool empty() const; /// Return isNull() bool operator!() const; /// Remove all object members and array elements. /// \pre type() is arrayValue, objectValue, or nullValue /// \post type() is unchanged void clear(); /// Resize the array to size elements. /// New elements are initialized to null. /// May only be called on nullValue or arrayValue. /// \pre type() is arrayValue or nullValue /// \post type() is arrayValue void resize(ArrayIndex size); /// Access an array element (zero based index ). /// If the array contains less than index element, then null value are /// inserted /// in the array so that its size is index+1. /// (You may need to say 'value[0u]' to get your compiler to distinguish /// this from the operator[] which takes a string.) Value& operator[](ArrayIndex index); /// Access an array element (zero based index ). /// If the array contains less than index element, then null value are /// inserted /// in the array so that its size is index+1. /// (You may need to say 'value[0u]' to get your compiler to distinguish /// this from the operator[] which takes a string.) Value& operator[](int index); /// Access an array element (zero based index ) /// (You may need to say 'value[0u]' to get your compiler to distinguish /// this from the operator[] which takes a string.) const Value& operator[](ArrayIndex index) const; /// Access an array element (zero based index ) /// (You may need to say 'value[0u]' to get your compiler to distinguish /// this from the operator[] which takes a string.) const Value& operator[](int index) const; /// If the array contains at least index+1 elements, returns the element /// value, /// otherwise returns defaultValue. Value get(ArrayIndex index, const Value& defaultValue) const; /// Return true if index < size(). bool isValidIndex(ArrayIndex index) const; /// \brief Append value to array at the end. /// /// Equivalent to jsonvalue[jsonvalue.size()] = value; Value& append(const Value& value); /// Access an object value by name, create a null member if it does not exist. /// \note Because of our implementation, keys are limited to 2^30 -1 chars. /// Exceeding that will cause an exception. Value& operator[](const char* key); /// Access an object value by name, returns null if there is no member with /// that name. const Value& operator[](const char* key) const; /// Access an object value by name, create a null member if it does not exist. /// \param key may contain embedded nulls. Value& operator[](const std::string& key); /// Access an object value by name, returns null if there is no member with /// that name. /// \param key may contain embedded nulls. const Value& operator[](const std::string& key) const; /** \brief Access an object value by name, create a null member if it does not exist. * If the object has no entry for that name, then the member name used to store * the new entry is not duplicated. * Example of use: * \code * Json::Value object; * static const StaticString code("code"); * object[code] = 1234; * \endcode */ Value& operator[](const StaticString& key); #ifdef JSON_USE_CPPTL /// Access an object value by name, create a null member if it does not exist. Value& operator[](const CppTL::ConstString& key); /// Access an object value by name, returns null if there is no member with /// that name. const Value& operator[](const CppTL::ConstString& key) const; #endif /// Return the member named key if it exist, defaultValue otherwise. /// \note deep copy Value get(const char* key, const Value& defaultValue) const; /// Return the member named key if it exist, defaultValue otherwise. /// \note deep copy /// \note key may contain embedded nulls. Value get(const char* begin, const char* end, const Value& defaultValue) const; /// Return the member named key if it exist, defaultValue otherwise. /// \note deep copy /// \param key may contain embedded nulls. Value get(const std::string& key, const Value& defaultValue) const; #ifdef JSON_USE_CPPTL /// Return the member named key if it exist, defaultValue otherwise. /// \note deep copy Value get(const CppTL::ConstString& key, const Value& defaultValue) const; #endif /// Most general and efficient version of isMember()const, get()const, /// and operator[]const /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 Value const* find(char const* begin, char const* end) const; /// Most general and efficient version of object-mutators. /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. Value const* demand(char const* begin, char const* end); /// \brief Remove and return the named member. /// /// Do nothing if it did not exist. /// \return the removed Value, or null. /// \pre type() is objectValue or nullValue /// \post type() is unchanged /// \deprecated Value removeMember(const char* key); /// Same as removeMember(const char*) /// \param key may contain embedded nulls. /// \deprecated Value removeMember(const std::string& key); /// Same as removeMember(const char* begin, const char* end, Value* removed), /// but 'key' is null-terminated. bool removeMember(const char* key, Value* removed); /** \brief Remove the named map member. Update 'removed' iff removed. \param key may contain embedded nulls. \return true iff removed (no exceptions) */ bool removeMember(std::string const& key, Value* removed); /// Same as removeMember(std::string const& key, Value* removed) bool removeMember(const char* begin, const char* end, Value* removed); /** \brief Remove the indexed array element. O(n) expensive operations. Update 'removed' iff removed. \return true iff removed (no exceptions) */ bool removeIndex(ArrayIndex i, Value* removed); /// Return true if the object has a member named key. /// \note 'key' must be null-terminated. bool isMember(const char* key) const; /// Return true if the object has a member named key. /// \param key may contain embedded nulls. bool isMember(const std::string& key) const; /// Same as isMember(std::string const& key)const bool isMember(const char* begin, const char* end) const; #ifdef JSON_USE_CPPTL /// Return true if the object has a member named key. bool isMember(const CppTL::ConstString& key) const; #endif /// \brief Return a list of the member names. /// /// If null, return an empty list. /// \pre type() is objectValue or nullValue /// \post if type() was nullValue, it remains nullValue Members getMemberNames() const; //# ifdef JSON_USE_CPPTL // EnumMemberNames enumMemberNames() const; // EnumValues enumValues() const; //# endif /// \deprecated Always pass len. JSONCPP_DEPRECATED("Use setComment(std::string const&) instead.") void setComment(const char* comment, CommentPlacement placement); /// Comments must be //... or /* ... */ void setComment(const char* comment, size_t len, CommentPlacement placement); /// Comments must be //... or /* ... */ void setComment(const std::string& comment, CommentPlacement placement); bool hasComment(CommentPlacement placement) const; /// Include delimiters and embedded newlines. std::string getComment(CommentPlacement placement) const; std::string toStyledString() const; const_iterator begin() const; const_iterator end() const; iterator begin(); iterator end(); private: void initBasic(ValueType type, bool allocated = false); Value& resolveReference(const char* key); Value& resolveReference(const char* key, const char* end); struct CommentInfo { CommentInfo(); ~CommentInfo(); void setComment(const char* text, size_t len); char* comment_; }; // struct MemberNamesTransform //{ // typedef const char *result_type; // const char *operator()( const CZString &name ) const // { // return name.c_str(); // } //}; union ValueHolder { LargestInt int_; LargestUInt uint_; double real_; bool bool_; char* string_; // actually ptr to unsigned, followed by str, unless !allocated_ ObjectValues* map_; } value_; ValueType type_ : 8; unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. // If not allocated_, string_ must be null-terminated. CommentInfo* comments_; }; /** \brief Experimental and untested: represents an element of the "path" to * access a node. */ class JSON_API PathArgument { public: friend class Path; PathArgument(); PathArgument(ArrayIndex index); PathArgument(const char* key); PathArgument(const std::string& key); private: enum Kind { kindNone = 0, kindIndex, kindKey }; std::string key_; ArrayIndex index_; Kind kind_; }; /** \brief Experimental and untested: represents a "path" to access a node. * * Syntax: * - "." => root node * - ".[n]" => elements at index 'n' of root node (an array value) * - ".name" => member named 'name' of root node (an object value) * - ".name1.name2.name3" * - ".[0][1][2].name1[3]" * - ".%" => member name is provided as parameter * - ".[%]" => index is provied as parameter */ class JSON_API Path { public: Path(const std::string& path, const PathArgument& a1 = PathArgument(), const PathArgument& a2 = PathArgument(), const PathArgument& a3 = PathArgument(), const PathArgument& a4 = PathArgument(), const PathArgument& a5 = PathArgument()); const Value& resolve(const Value& root) const; Value resolve(const Value& root, const Value& defaultValue) const; /// Creates the "path" to access the specified node and returns a reference on /// the node. Value& make(Value& root) const; private: typedef std::vector InArgs; typedef std::vector Args; void makePath(const std::string& path, const InArgs& in); void addPathInArg(const std::string& path, const InArgs& in, InArgs::const_iterator& itInArg, PathArgument::Kind kind); void invalidPath(const std::string& path, int location); Args args_; }; /** \brief base class for Value iterators. * */ class JSON_API ValueIteratorBase { public: typedef std::bidirectional_iterator_tag iterator_category; typedef unsigned int size_t; typedef int difference_type; typedef ValueIteratorBase SelfType; bool operator==(const SelfType& other) const { return isEqual(other); } bool operator!=(const SelfType& other) const { return !isEqual(other); } difference_type operator-(const SelfType& other) const { return other.computeDistance(*this); } /// Return either the index or the member name of the referenced value as a /// Value. Value key() const; /// Return the index of the referenced Value, or -1 if it is not an arrayValue. UInt index() const; /// Return the member name of the referenced Value, or "" if it is not an /// objectValue. /// \note Avoid `c_str()` on result, as embedded zeroes are possible. std::string name() const; /// Return the member name of the referenced Value. "" if it is not an /// objectValue. /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. JSONCPP_DEPRECATED("Use `key = name();` instead.") char const* memberName() const; /// Return the member name of the referenced Value, or NULL if it is not an /// objectValue. /// \note Better version than memberName(). Allows embedded nulls. char const* memberName(char const** end) const; protected: Value& deref() const; void increment(); void decrement(); difference_type computeDistance(const SelfType& other) const; bool isEqual(const SelfType& other) const; void copy(const SelfType& other); private: Value::ObjectValues::iterator current_; // Indicates that iterator is for a null value. bool isNull_; public: // For some reason, BORLAND needs these at the end, rather // than earlier. No idea why. ValueIteratorBase(); explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); }; /** \brief const iterator for object and array value. * */ class JSON_API ValueConstIterator : public ValueIteratorBase { friend class Value; public: typedef const Value value_type; //typedef unsigned int size_t; //typedef int difference_type; typedef const Value& reference; typedef const Value* pointer; typedef ValueConstIterator SelfType; ValueConstIterator(); private: /*! \internal Use by Value to create an iterator. */ explicit ValueConstIterator(const Value::ObjectValues::iterator& current); public: SelfType& operator=(const ValueIteratorBase& other); SelfType operator++(int) { SelfType temp(*this); ++*this; return temp; } SelfType operator--(int) { SelfType temp(*this); --*this; return temp; } SelfType& operator--() { decrement(); return *this; } SelfType& operator++() { increment(); return *this; } reference operator*() const { return deref(); } pointer operator->() const { return &deref(); } }; /** \brief Iterator for object and array value. */ class JSON_API ValueIterator : public ValueIteratorBase { friend class Value; public: typedef Value value_type; typedef unsigned int size_t; typedef int difference_type; typedef Value& reference; typedef Value* pointer; typedef ValueIterator SelfType; ValueIterator(); ValueIterator(const ValueConstIterator& other); ValueIterator(const ValueIterator& other); private: /*! \internal Use by Value to create an iterator. */ explicit ValueIterator(const Value::ObjectValues::iterator& current); public: SelfType& operator=(const SelfType& other); SelfType operator++(int) { SelfType temp(*this); ++*this; return temp; } SelfType operator--(int) { SelfType temp(*this); --*this; return temp; } SelfType& operator--() { decrement(); return *this; } SelfType& operator++() { increment(); return *this; } reference operator*() const { return deref(); } pointer operator->() const { return &deref(); } }; } // namespace Json namespace std { /// Specialize std::swap() for Json::Value. template<> inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } } #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #pragma warning(pop) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #endif // CPPTL_JSON_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/value.h // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: include/json/reader.h // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef CPPTL_JSON_READER_H_INCLUDED #define CPPTL_JSON_READER_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) #include "features.h" #include "value.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include #include #include #include // Disable warning C4251: : needs to have dll-interface to // be used by... #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #pragma warning(push) #pragma warning(disable : 4251) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) namespace Json { /** \brief Unserialize a JSON document into a *Value. * * \deprecated Use CharReader and CharReaderBuilder. */ class JSON_API Reader { public: typedef char Char; typedef const Char* Location; /** \brief Constructs a Reader allowing all features * for parsing. */ Reader(); /** \brief Constructs a Reader allowing the specified feature set * for parsing. */ Reader(const Features& features); /** \brief Read a Value from a JSON * document. * \param document UTF-8 encoded string containing the document to read. * \param root [out] Contains the root value of the document if it was * successfully parsed. * \param collectComments \c true to collect comment and allow writing them * back during * serialization, \c false to discard comments. * This parameter is ignored if * Features::allowComments_ * is \c false. * \return \c true if the document was successfully parsed, \c false if an * error occurred. */ bool parse(const std::string& document, Value& root, bool collectComments = true); /** \brief Read a Value from a JSON document. * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the document to read. * \param endDoc Pointer on the end of the UTF-8 encoded string of the document to read. * Must be >= beginDoc. * \param root [out] Contains the root value of the document if it was * successfully parsed. * \param collectComments \c true to collect comment and allow writing them back during * serialization, \c false to discard comments. * This parameter is ignored if Features::allowComments_ * is \c false. * \return \c true if the document was successfully parsed, \c false if an error occurred. */ bool parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true); /// \brief Parse from input stream. /// \see Json::operator>>(std::istream&, Json::Value&). bool parse(std::istream& is, Value& root, bool collectComments = true); /** \brief Returns a user friendly string that list errors in the parsed * document. * \return Formatted error message with the list of errors with their location * in * the parsed document. An empty string is returned if no error * occurred * during parsing. * \deprecated Use getFormattedErrorMessages() instead (typo fix). */ JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") std::string getFormatedErrorMessages() const; /** \brief Returns a user friendly string that list errors in the parsed * document. * \return Formatted error message with the list of errors with their location * in * the parsed document. An empty string is returned if no error * occurred * during parsing. */ std::string getFormattedErrorMessages() const; private: enum TokenType { tokenEndOfStream = 0, tokenObjectBegin, tokenObjectEnd, tokenArrayBegin, tokenArrayEnd, tokenString, tokenNumber, tokenTrue, tokenFalse, tokenNull, tokenArraySeparator, tokenMemberSeparator, tokenComment, tokenError }; class Token { public: TokenType type_; Location start_; Location end_; }; class ErrorInfo { public: Token token_; std::string message_; Location extra_; }; typedef std::deque Errors; bool readToken(Token& token); void skipSpaces(); bool match(Location pattern, int patternLength); bool readComment(); bool readCStyleComment(); bool readCppStyleComment(); bool readString(); void readNumber(); bool readValue(); bool readObject(Token& token); bool readArray(Token& token); bool decodeNumber(Token& token); bool decodeNumber(Token& token, Value& decoded); bool decodeString(Token& token); bool decodeString(Token& token, std::string& decoded); bool decodeDouble(Token& token); bool decodeDouble(Token& token, Value& decoded); bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode); bool decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, unsigned int& unicode); bool addError(const std::string& message, Token& token, Location extra = 0); bool recoverFromError(TokenType skipUntilToken); bool addErrorAndRecover(const std::string& message, Token& token, TokenType skipUntilToken); void skipUntilSpace(); Value& currentValue(); Char getNextChar(); void getLocationLineAndColumn(Location location, int& line, int& column) const; std::string getLocationLineAndColumn(Location location) const; void addComment(Location begin, Location end, CommentPlacement placement); void skipCommentTokens(Token& token); typedef std::stack Nodes; Nodes nodes_; Errors errors_; std::string document_; Location begin_; Location end_; Location current_; Location lastValueEnd_; Value* lastValue_; std::string commentsBefore_; Features features_; bool collectComments_; }; // Reader /** Interface for reading JSON from a char array. */ class JSON_API CharReader { public: virtual ~CharReader() {} /** \brief Read a Value from a JSON document. * The document must be a UTF-8 encoded string containing the document to read. * * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the document to read. * \param endDoc Pointer on the end of the UTF-8 encoded string of the document to read. * Must be >= beginDoc. * \param root [out] Contains the root value of the document if it was * successfully parsed. * \param errs [out] Formatted error messages (if not NULL) * a user friendly string that lists errors in the parsed * document. * \return \c true if the document was successfully parsed, \c false if an error occurred. */ virtual bool parse( char const* beginDoc, char const* endDoc, Value* root, std::string* errs) = 0; class Factory { public: virtual ~Factory() {} /** \brief Allocate a CharReader via operator new(). * \throw std::exception if something goes wrong (e.g. invalid settings) */ virtual CharReader* newCharReader() const = 0; }; // Factory }; // CharReader /** \brief Build a CharReader implementation. Usage: \code using namespace Json; CharReaderBuilder builder; builder["collectComments"] = false; Value value; std::string errs; bool ok = parseFromStream(builder, std::cin, &value, &errs); \endcode */ class JSON_API CharReaderBuilder : public CharReader::Factory { public: // Note: We use a Json::Value so that we can add data-members to this class // without a major version bump. /** Configuration of this builder. These are case-sensitive. Available settings (case-sensitive): - `"collectComments": false or true` - true to collect comment and allow writing them back during serialization, false to discard comments. This parameter is ignored if allowComments is false. - `"allowComments": false or true` - true if comments are allowed. - `"strictRoot": false or true` - true if root must be either an array or an object value - `"allowDroppedNullPlaceholders": false or true` - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) - `"allowNumericKeys": false or true` - true if numeric object keys are allowed. - `"allowSingleQuotes": false or true` - true if '' are allowed for strings (both keys and values) - `"stackLimit": integer` - Exceeding stackLimit (recursive depth of `readValue()`) will cause an exception. - This is a security issue (seg-faults caused by deeply nested JSON), so the default is low. - `"failIfExtra": false or true` - If true, `parse()` returns false when extra non-whitespace trails the JSON value in the input string. - `"rejectDupKeys": false or true` - If true, `parse()` returns false when a key is duplicated within an object. - `"allowSpecialFloats": false or true` - If true, special float values (NaNs and infinities) are allowed and their values are lossfree restorable. You can examine 'settings_` yourself to see the defaults. You can also write and read them just like any JSON Value. \sa setDefaults() */ Json::Value settings_; CharReaderBuilder(); virtual ~CharReaderBuilder(); virtual CharReader* newCharReader() const; /** \return true if 'settings' are legal and consistent; * otherwise, indicate bad settings via 'invalid'. */ bool validate(Json::Value* invalid) const; /** A simple way to update a specific setting. */ Value& operator[](std::string key); /** Called by ctor, but you can use this to reset settings_. * \pre 'settings' != NULL (but Json::null is fine) * \remark Defaults: * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults */ static void setDefaults(Json::Value* settings); /** Same as old Features::strictMode(). * \pre 'settings' != NULL (but Json::null is fine) * \remark Defaults: * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode */ static void strictMode(Json::Value* settings); }; /** Consume entire stream and use its begin/end. * Someday we might have a real StreamReader, but for now this * is convenient. */ bool JSON_API parseFromStream( CharReader::Factory const&, std::istream&, Value* root, std::string* errs); /** \brief Read from 'sin' into 'root'. Always keep comments from the input JSON. This can be used to read a file into a particular sub-object. For example: \code Json::Value root; cin >> root["dir"]["file"]; cout << root; \endcode Result: \verbatim { "dir": { "file": { // The input stream JSON would be nested here. } } } \endverbatim \throw std::exception on parse error. \see Json::operator<<() */ JSON_API std::istream& operator>>(std::istream&, Value&); } // namespace Json #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #pragma warning(pop) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #endif // CPPTL_JSON_READER_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/reader.h // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: include/json/writer.h // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef JSON_WRITER_H_INCLUDED #define JSON_WRITER_H_INCLUDED #if !defined(JSON_IS_AMALGAMATION) #include "value.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include #include // Disable warning C4251: : needs to have dll-interface to // be used by... #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #pragma warning(push) #pragma warning(disable : 4251) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) namespace Json { class Value; /** Usage: \code using namespace Json; void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { std::unique_ptr const writer( factory.newStreamWriter()); writer->write(value, &std::cout); std::cout << std::endl; // add lf and flush } \endcode */ class JSON_API StreamWriter { protected: std::ostream* sout_; // not owned; will not delete public: StreamWriter(); virtual ~StreamWriter(); /** Write Value into document as configured in sub-class. Do not take ownership of sout, but maintain a reference during function. \pre sout != NULL \return zero on success (For now, we always return zero, so check the stream instead.) \throw std::exception possibly, depending on configuration */ virtual int write(Value const& root, std::ostream* sout) = 0; /** \brief A simple abstract factory. */ class JSON_API Factory { public: virtual ~Factory(); /** \brief Allocate a CharReader via operator new(). * \throw std::exception if something goes wrong (e.g. invalid settings) */ virtual StreamWriter* newStreamWriter() const = 0; }; // Factory }; // StreamWriter /** \brief Write into stringstream, then return string, for convenience. * A StreamWriter will be created from the factory, used, and then deleted. */ std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); /** \brief Build a StreamWriter implementation. Usage: \code using namespace Json; Value value = ...; StreamWriterBuilder builder; builder["commentStyle"] = "None"; builder["indentation"] = " "; // or whatever you like std::unique_ptr writer( builder.newStreamWriter()); writer->write(value, &std::cout); std::cout << std::endl; // add lf and flush \endcode */ class JSON_API StreamWriterBuilder : public StreamWriter::Factory { public: // Note: We use a Json::Value so that we can add data-members to this class // without a major version bump. /** Configuration of this builder. Available settings (case-sensitive): - "commentStyle": "None" or "All" - "indentation": "" - "enableYAMLCompatibility": false or true - slightly change the whitespace around colons - "dropNullPlaceholders": false or true - Drop the "null" string from the writer's output for nullValues. Strictly speaking, this is not valid JSON. But when the output is being fed to a browser's Javascript, it makes for smaller output and the browser can handle the output just fine. - "useSpecialFloats": false or true - If true, outputs non-finite floating point values in the following way: NaN values as "NaN", positive infinity as "Infinity", and negative infinity as "-Infinity". You can examine 'settings_` yourself to see the defaults. You can also write and read them just like any JSON Value. \sa setDefaults() */ Json::Value settings_; StreamWriterBuilder(); virtual ~StreamWriterBuilder(); /** * \throw std::exception if something goes wrong (e.g. invalid settings) */ virtual StreamWriter* newStreamWriter() const; /** \return true if 'settings' are legal and consistent; * otherwise, indicate bad settings via 'invalid'. */ bool validate(Json::Value* invalid) const; /** A simple way to update a specific setting. */ Value& operator[](std::string key); /** Called by ctor, but you can use this to reset settings_. * \pre 'settings' != NULL (but Json::null is fine) * \remark Defaults: * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults */ static void setDefaults(Json::Value* settings); }; /** \brief Abstract class for writers. * \deprecated Use StreamWriter. (And really, this is an implementation detail.) */ class JSON_API Writer { public: virtual ~Writer(); virtual std::string write(const Value& root) = 0; }; /** \brief Outputs a Value in JSON format *without formatting (not human friendly). * * The JSON document is written in a single line. It is not intended for 'human' *consumption, * but may be usefull to support feature such as RPC where bandwith is limited. * \sa Reader, Value * \deprecated Use StreamWriterBuilder. */ class JSON_API FastWriter : public Writer { public: FastWriter(); virtual ~FastWriter() {} void enableYAMLCompatibility(); public: // overridden from Writer virtual std::string write(const Value& root); private: void writeValue(const Value& value); std::string document_; bool yamlCompatiblityEnabled_; }; /** \brief Writes a Value in JSON format in a *human friendly way. * * The rules for line break and indent are as follow: * - Object value: * - if empty then print {} without indent and line break * - if not empty the print '{', line break & indent, print one value per *line * and then unindent and line break and print '}'. * - Array value: * - if empty then print [] without indent and line break * - if the array contains no object value, empty array or some other value *types, * and all the values fit on one lines, then print the array on a single *line. * - otherwise, it the values do not fit on one line, or the array contains * object or non empty array, then print one value per line. * * If the Value have comments then they are outputed according to their *#CommentPlacement. * * \sa Reader, Value, Value::setComment() * \deprecated Use StreamWriterBuilder. */ class JSON_API StyledWriter : public Writer { public: StyledWriter(); virtual ~StyledWriter() {} public: // overridden from Writer /** \brief Serialize a Value in JSON format. * \param root Value to serialize. * \return String containing the JSON document that represents the root value. */ virtual std::string write(const Value& root); private: void writeValue(const Value& value); void writeArrayValue(const Value& value); bool isMultineArray(const Value& value); void pushValue(const std::string& value); void writeIndent(); void writeWithIndent(const std::string& value); void indent(); void unindent(); void writeCommentBeforeValue(const Value& root); void writeCommentAfterValueOnSameLine(const Value& root); bool hasCommentForValue(const Value& value); static std::string normalizeEOL(const std::string& text); typedef std::vector ChildValues; ChildValues childValues_; std::string document_; std::string indentString_; int rightMargin_; int indentSize_; bool addChildValues_; }; /** \brief Writes a Value in JSON format in a human friendly way, to a stream rather than to a string. * * The rules for line break and indent are as follow: * - Object value: * - if empty then print {} without indent and line break * - if not empty the print '{', line break & indent, print one value per line * and then unindent and line break and print '}'. * - Array value: * - if empty then print [] without indent and line break * - if the array contains no object value, empty array or some other value types, * and all the values fit on one lines, then print the array on a single line. * - otherwise, it the values do not fit on one line, or the array contains * object or non empty array, then print one value per line. * * If the Value have comments then they are outputed according to their #CommentPlacement. * * \param indentation Each level will be indented by this amount extra. * \sa Reader, Value, Value::setComment() * \deprecated Use StreamWriterBuilder. */ class JSON_API StyledStreamWriter { public: StyledStreamWriter(std::string indentation = "\t"); ~StyledStreamWriter() {} public: /** \brief Serialize a Value in JSON format. * \param out Stream to write to. (Can be ostringstream, e.g.) * \param root Value to serialize. * \note There is no point in deriving from Writer, since write() should not * return a value. */ void write(std::ostream& out, const Value& root); private: void writeValue(const Value& value); void writeArrayValue(const Value& value); bool isMultineArray(const Value& value); void pushValue(const std::string& value); void writeIndent(); void writeWithIndent(const std::string& value); void indent(); void unindent(); void writeCommentBeforeValue(const Value& root); void writeCommentAfterValueOnSameLine(const Value& root); bool hasCommentForValue(const Value& value); static std::string normalizeEOL(const std::string& text); typedef std::vector ChildValues; ChildValues childValues_; std::ostream* document_; std::string indentString_; int rightMargin_; std::string indentation_; bool addChildValues_ : 1; bool indented_ : 1; }; #if defined(JSON_HAS_INT64) std::string JSON_API valueToString(Int value); std::string JSON_API valueToString(UInt value); #endif // if defined(JSON_HAS_INT64) std::string JSON_API valueToString(LargestInt value); std::string JSON_API valueToString(LargestUInt value); std::string JSON_API valueToString(double value); std::string JSON_API valueToString(bool value); std::string JSON_API valueToQuotedString(const char* value); /// \brief Output using the StyledStreamWriter. /// \see Json::operator>>() JSON_API std::ostream& operator<<(std::ostream&, const Value& root); } // namespace Json #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #pragma warning(pop) #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) #endif // JSON_WRITER_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/writer.h // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: include/json/assertions.h // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED #define CPPTL_JSON_ASSERTIONS_H_INCLUDED #include #include #if !defined(JSON_IS_AMALGAMATION) #include "config.h" #endif // if !defined(JSON_IS_AMALGAMATION) /** It should not be possible for a maliciously designed file to * cause an abort() or seg-fault, so these macros are used only * for pre-condition violations and internal logic errors. */ #if JSON_USE_EXCEPTION // @todo <= add detail about condition in exception # define JSON_ASSERT(condition) \ {if (!(condition)) {Json::throwLogicError( "assert json failed" );}} # define JSON_FAIL_MESSAGE(message) \ { \ std::ostringstream oss; oss << message; \ Json::throwLogicError(oss.str()); \ abort(); \ } #else // JSON_USE_EXCEPTION # define JSON_ASSERT(condition) assert(condition) // The call to assert() will show the failure message in debug builds. In // release builds we abort, for a core-dump or debugger. # define JSON_FAIL_MESSAGE(message) \ { \ std::ostringstream oss; oss << message; \ assert(false && oss.str().c_str()); \ abort(); \ } #endif #define JSON_ASSERT_MESSAGE(condition, message) \ if (!(condition)) { \ JSON_FAIL_MESSAGE(message); \ } #endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/assertions.h // ////////////////////////////////////////////////////////////////////// #endif //ifndef JSON_AMALGATED_H_INCLUDED sysdig-0.26.4/userspace/libsinsp/third-party/jsoncpp/jsoncpp.cpp000066400000000000000000004242721352731327100250440ustar00rootroot00000000000000/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/). /// It is intended to be used with #include "json/json.h" // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: LICENSE // ////////////////////////////////////////////////////////////////////// /* The JsonCpp library's source code, including accompanying documentation, tests and demonstration applications, are licensed under the following conditions... The author (Baptiste Lepilleur) explicitly disclaims copyright in all jurisdictions which recognize such a disclaimer. In such jurisdictions, this software is released into the Public Domain. In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is released under the terms of the MIT License (see below). In jurisdictions which recognize Public Domain property, the user of this software may choose to accept it either as 1) Public Domain, 2) under the conditions of the MIT License (see below), or 3) under the terms of dual Public Domain/MIT License conditions described here, as they choose. The MIT License is about as close to Public Domain as a license can get, and is described in clear, concise terms at: http://en.wikipedia.org/wiki/MIT_License The full text of the MIT License follows: ======================================================================== Copyright (c) 2007-2010 Baptiste Lepilleur Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ======================================================================== (END LICENSE TEXT) The MIT license is compatible with both the GPL and commercial software, affording one all of the rights of Public Domain with the minor nuisance of being required to keep the above copyright notice and license text in the source code. Note also that by accepting the Public Domain "license" you can re-license your copy using whatever license you like. */ // ////////////////////////////////////////////////////////////////////// // End of content of file: LICENSE // ////////////////////////////////////////////////////////////////////// #include "json/json.h" #ifndef JSON_IS_AMALGAMATION #error "Compile with -I PATH_TO_JSON_DIRECTORY" #endif // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: src/lib_json/json_tool.h // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED #define LIB_JSONCPP_JSON_TOOL_H_INCLUDED /* This header provides common string manipulation support, such as UTF-8, * portable conversion from/to string... * * It is an internal header that must not be exposed. */ namespace Json { /// Converts a unicode code-point to UTF-8. static inline std::string codePointToUTF8(unsigned int cp) { std::string result; // based on description from http://en.wikipedia.org/wiki/UTF-8 if (cp <= 0x7f) { result.resize(1); result[0] = static_cast(cp); } else if (cp <= 0x7FF) { result.resize(2); result[1] = static_cast(0x80 | (0x3f & cp)); result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); } else if (cp <= 0xFFFF) { result.resize(3); result[2] = static_cast(0x80 | (0x3f & cp)); result[1] = static_cast(0x80 | (0x3f & (cp >> 6))); result[0] = static_cast(0xE0 | (0xf & (cp >> 12))); } else if (cp <= 0x10FFFF) { result.resize(4); result[3] = static_cast(0x80 | (0x3f & cp)); result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); } return result; } /// Returns true if ch is a control character (in range [1,31]). static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; } enum { /// Constant that specify the size of the buffer that must be passed to /// uintToString. uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 }; // Defines a char buffer for use with uintToString(). typedef char UIntToStringBuffer[uintToStringBufferSize]; /** Converts an unsigned integer to string. * @param value Unsigned interger to convert to string * @param current Input/Output string buffer. * Must have at least uintToStringBufferSize chars free. */ static inline void uintToString(LargestUInt value, char*& current) { *--current = 0; do { *--current = static_cast(value % 10U + static_cast('0')); value /= 10; } while (value != 0); } /** Change ',' to '.' everywhere in buffer. * * We had a sophisticated way, but it did not work in WinCE. * @see https://github.com/open-source-parsers/jsoncpp/pull/9 */ static inline void fixNumericLocale(char* begin, char* end) { while (begin < end) { if (*begin == ',') { *begin = '.'; } ++begin; } } } // namespace Json { #endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: src/lib_json/json_tool.h // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: src/lib_json/json_reader.cpp // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2011 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) #include #include #include #include "json_tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include #include #include #include #include #include #include #include #if defined(_MSC_VER) #if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above #define snprintf sprintf_s #elif _MSC_VER >= 1900 // VC++ 14.0 and above #define snprintf std::snprintf #else #define snprintf _snprintf #endif #elif defined(__ANDROID__) #define snprintf snprintf #elif __cplusplus >= 201103L #define snprintf std::snprintf #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 // Disable warning about strdup being deprecated. #pragma warning(disable : 4996) #endif static int const stackLimit_g = 1000; static int stackDepth_g = 0; // see readValue() namespace Json { #if __cplusplus >= 201103L typedef std::unique_ptr const CharReaderPtr; #else typedef std::auto_ptr CharReaderPtr; #endif // Implementation of class Features // //////////////////////////////// Features::Features() : allowComments_(true), strictRoot_(false) {} Features Features::all() { return Features(); } Features Features::strictMode() { Features features; features.allowComments_ = false; features.strictRoot_ = true; return features; } // Implementation of class Reader // //////////////////////////////// static bool containsNewLine(Reader::Location begin, Reader::Location end) { for (; begin < end; ++begin) if (*begin == '\n' || *begin == '\r') return true; return false; } // Class Reader // ////////////////////////////////////////////////////////////////// Reader::Reader() : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), lastValue_(), commentsBefore_(), features_(Features::all()), collectComments_() {} Reader::Reader(const Features& features) : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), lastValue_(), commentsBefore_(), features_(features), collectComments_() { } bool Reader::parse(const std::string& document, Value& root, bool collectComments) { document_ = document; const char* begin = document_.c_str(); const char* end = begin + document_.length(); return parse(begin, end, root, collectComments); } bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { // std::istream_iterator begin(sin); // std::istream_iterator end; // Those would allow streamed input from a file, if parse() were a // template function. // Since std::string is reference-counted, this at least does not // create an extra copy. std::string doc; std::getline(sin, doc, (char)EOF); return parse(doc, root, collectComments); } bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments) { if (!features_.allowComments_) { collectComments = false; } begin_ = beginDoc; end_ = endDoc; collectComments_ = collectComments; current_ = begin_; lastValueEnd_ = 0; lastValue_ = 0; commentsBefore_ = ""; errors_.clear(); while (!nodes_.empty()) nodes_.pop(); nodes_.push(&root); stackDepth_g = 0; // Yes, this is bad coding, but options are limited. bool successful = readValue(); Token token; skipCommentTokens(token); if (collectComments_ && !commentsBefore_.empty()) root.setComment(commentsBefore_, commentAfter); if (features_.strictRoot_) { if (!root.isArray() && !root.isObject()) { // Set error location to start of doc, ideally should be first token found // in doc token.type_ = tokenError; token.start_ = beginDoc; token.end_ = endDoc; addError( "A valid JSON document must be either an array or an object value.", token); return false; } } return successful; } bool Reader::readValue() { // This is a non-reentrant way to support a stackLimit. Terrible! // But this deprecated class has a security problem: Bad input can // cause a seg-fault. This seems like a fair, binary-compatible way // to prevent the problem. if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); ++stackDepth_g; Token token; skipCommentTokens(token); bool successful = true; if (collectComments_ && !commentsBefore_.empty()) { currentValue().setComment(commentsBefore_, commentBefore); commentsBefore_ = ""; } switch (token.type_) { case tokenObjectBegin: successful = readObject(token); break; case tokenArrayBegin: successful = readArray(token); break; case tokenNumber: successful = decodeNumber(token); break; case tokenString: successful = decodeString(token); break; case tokenTrue: { Value v(true); currentValue().swapPayload(v); } break; case tokenFalse: { Value v(false); currentValue().swapPayload(v); } break; case tokenNull: { Value v; currentValue().swapPayload(v); } break; // Else, fall through... default: return addError("Syntax error: value, object or array expected.", token); } if (collectComments_) { lastValueEnd_ = current_; lastValue_ = ¤tValue(); } --stackDepth_g; return successful; } void Reader::skipCommentTokens(Token& token) { if (features_.allowComments_) { do { readToken(token); } while (token.type_ == tokenComment); } else { readToken(token); } } bool Reader::readToken(Token& token) { skipSpaces(); token.start_ = current_; Char c = getNextChar(); bool ok = true; switch (c) { case '{': token.type_ = tokenObjectBegin; break; case '}': token.type_ = tokenObjectEnd; break; case '[': token.type_ = tokenArrayBegin; break; case ']': token.type_ = tokenArrayEnd; break; case '"': token.type_ = tokenString; ok = readString(); break; case '/': token.type_ = tokenComment; ok = readComment(); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': token.type_ = tokenNumber; readNumber(); break; case 't': token.type_ = tokenTrue; ok = match("rue", 3); break; case 'f': token.type_ = tokenFalse; ok = match("alse", 4); break; case 'n': token.type_ = tokenNull; ok = match("ull", 3); break; case ',': token.type_ = tokenArraySeparator; break; case ':': token.type_ = tokenMemberSeparator; break; case 0: token.type_ = tokenEndOfStream; break; default: ok = false; break; } if (!ok) token.type_ = tokenError; token.end_ = current_; return true; } void Reader::skipSpaces() { while (current_ != end_) { Char c = *current_; if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++current_; else break; } } bool Reader::match(Location pattern, int patternLength) { if (end_ - current_ < patternLength) return false; int index = patternLength; while (index--) if (current_[index] != pattern[index]) return false; current_ += patternLength; return true; } bool Reader::readComment() { Location commentBegin = current_ - 1; Char c = getNextChar(); bool successful = false; if (c == '*') successful = readCStyleComment(); else if (c == '/') successful = readCppStyleComment(); if (!successful) return false; if (collectComments_) { CommentPlacement placement = commentBefore; if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { if (c != '*' || !containsNewLine(commentBegin, current_)) placement = commentAfterOnSameLine; } addComment(commentBegin, current_, placement); } return true; } static std::string normalizeEOL(Reader::Location begin, Reader::Location end) { std::string normalized; normalized.reserve(end - begin); Reader::Location current = begin; while (current != end) { char c = *current++; if (c == '\r') { if (current != end && *current == '\n') // convert dos EOL ++current; // convert Mac EOL normalized += '\n'; } else { normalized += c; } } return normalized; } void Reader::addComment(Location begin, Location end, CommentPlacement placement) { assert(collectComments_); const std::string& normalized = normalizeEOL(begin, end); if (placement == commentAfterOnSameLine) { assert(lastValue_ != 0); lastValue_->setComment(normalized, placement); } else { commentsBefore_ += normalized; } } bool Reader::readCStyleComment() { while (current_ != end_) { Char c = getNextChar(); if (c == '*' && *current_ == '/') break; } return getNextChar() == '/'; } bool Reader::readCppStyleComment() { while (current_ != end_) { Char c = getNextChar(); if (c == '\n') break; if (c == '\r') { // Consume DOS EOL. It will be normalized in addComment. if (current_ != end_ && *current_ == '\n') getNextChar(); // Break on Moc OS 9 EOL. break; } } return true; } void Reader::readNumber() { const char *p = current_; char c = '0'; // stopgap for already consumed character // integral part while (c >= '0' && c <= '9') c = (current_ = p) < end_ ? *p++ : 0; // fractional part if (c == '.') { c = (current_ = p) < end_ ? *p++ : 0; while (c >= '0' && c <= '9') c = (current_ = p) < end_ ? *p++ : 0; } // exponential part if (c == 'e' || c == 'E') { c = (current_ = p) < end_ ? *p++ : 0; if (c == '+' || c == '-') c = (current_ = p) < end_ ? *p++ : 0; while (c >= '0' && c <= '9') c = (current_ = p) < end_ ? *p++ : 0; } } bool Reader::readString() { Char c = 0; while (current_ != end_) { c = getNextChar(); if (c == '\\') getNextChar(); else if (c == '"') break; } return c == '"'; } bool Reader::readObject(Token& /*tokenStart*/) { Token tokenName; std::string name; Value init(objectValue); currentValue().swapPayload(init); while (readToken(tokenName)) { bool initialTokenOk = true; while (tokenName.type_ == tokenComment && initialTokenOk) initialTokenOk = readToken(tokenName); if (!initialTokenOk) break; if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object return true; name = ""; if (tokenName.type_ == tokenString) { if (!decodeString(tokenName, name)) return recoverFromError(tokenObjectEnd); } else { break; } Token colon; if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { return addErrorAndRecover( "Missing ':' after object member name", colon, tokenObjectEnd); } Value& value = currentValue()[name]; nodes_.push(&value); bool ok = readValue(); nodes_.pop(); if (!ok) // error already set return recoverFromError(tokenObjectEnd); Token comma; if (!readToken(comma) || (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && comma.type_ != tokenComment)) { return addErrorAndRecover( "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); } bool finalizeTokenOk = true; while (comma.type_ == tokenComment && finalizeTokenOk) finalizeTokenOk = readToken(comma); if (comma.type_ == tokenObjectEnd) return true; } return addErrorAndRecover( "Missing '}' or object member name", tokenName, tokenObjectEnd); } bool Reader::readArray(Token& /*tokenStart*/) { Value init(arrayValue); currentValue().swapPayload(init); skipSpaces(); if (*current_ == ']') // empty array { Token endArray; readToken(endArray); return true; } int index = 0; for (;;) { Value& value = currentValue()[index++]; nodes_.push(&value); bool ok = readValue(); nodes_.pop(); if (!ok) // error already set return recoverFromError(tokenArrayEnd); Token token; // Accept Comment after last item in the array. ok = readToken(token); while (token.type_ == tokenComment && ok) { ok = readToken(token); } bool badTokenType = (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); if (!ok || badTokenType) { return addErrorAndRecover( "Missing ',' or ']' in array declaration", token, tokenArrayEnd); } if (token.type_ == tokenArrayEnd) break; } return true; } bool Reader::decodeNumber(Token& token) { Value decoded; if (!decodeNumber(token, decoded)) return false; currentValue().swapPayload(decoded); return true; } bool Reader::decodeNumber(Token& token, Value& decoded) { // Attempts to parse the number as an integer. If the number is // larger than the maximum supported value of an integer then // we decode the number as a double. Location current = token.start_; bool isNegative = *current == '-'; if (isNegative) ++current; // TODO: Help the compiler do the div and mod at compile time or get rid of them. Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 : Value::maxLargestUInt; Value::LargestUInt threshold = maxIntegerValue / 10; Value::LargestUInt value = 0; while (current < token.end_) { Char c = *current++; if (c < '0' || c > '9') return decodeDouble(token, decoded); Value::UInt digit(c - '0'); if (value >= threshold) { // We've hit or exceeded the max value divided by 10 (rounded down). If // a) we've only just touched the limit, b) this is the last digit, and // c) it's small enough to fit in that rounding delta, we're okay. // Otherwise treat this number as a double to avoid overflow. if (value > threshold || current != token.end_ || digit > maxIntegerValue % 10) { return decodeDouble(token, decoded); } } value = value * 10 + digit; } if (isNegative && value == maxIntegerValue) decoded = Value::minLargestInt; else if (isNegative) decoded = -Value::LargestInt(value); else if (value <= Value::LargestUInt(Value::maxInt)) decoded = Value::LargestInt(value); else decoded = value; return true; } bool Reader::decodeDouble(Token& token) { Value decoded; if (!decodeDouble(token, decoded)) return false; currentValue().swapPayload(decoded); return true; } bool Reader::decodeDouble(Token& token, Value& decoded) { double value = 0; std::string buffer(token.start_, token.end_); std::istringstream is(buffer); if (!(is >> value)) return addError("'" + std::string(token.start_, token.end_) + "' is not a number.", token); decoded = value; return true; } bool Reader::decodeString(Token& token) { std::string decoded_string; if (!decodeString(token, decoded_string)) return false; Value decoded(decoded_string); currentValue().swapPayload(decoded); return true; } bool Reader::decodeString(Token& token, std::string& decoded) { decoded.reserve(token.end_ - token.start_ - 2); Location current = token.start_ + 1; // skip '"' Location end = token.end_ - 1; // do not include '"' while (current != end) { Char c = *current++; if (c == '"') break; else if (c == '\\') { if (current == end) return addError("Empty escape sequence in string", token, current); Char escape = *current++; switch (escape) { case '"': decoded += '"'; break; case '/': decoded += '/'; break; case '\\': decoded += '\\'; break; case 'b': decoded += '\b'; break; case 'f': decoded += '\f'; break; case 'n': decoded += '\n'; break; case 'r': decoded += '\r'; break; case 't': decoded += '\t'; break; case 'u': { unsigned int unicode; if (!decodeUnicodeCodePoint(token, current, end, unicode)) return false; decoded += codePointToUTF8(unicode); } break; default: return addError("Bad escape sequence in string", token, current); } } else { decoded += c; } } return true; } bool Reader::decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode) { if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) return false; if (unicode >= 0xD800 && unicode <= 0xDBFF) { // surrogate pairs if (end - current < 6) return addError( "additional six characters expected to parse unicode surrogate pair.", token, current); unsigned int surrogatePair; if (*(current++) == '\\' && *(current++) == 'u') { if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); } else return false; } else return addError("expecting another \\u token to begin the second half of " "a unicode surrogate pair", token, current); } return true; } bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, unsigned int& unicode) { if (end - current < 4) return addError( "Bad unicode escape sequence in string: four digits expected.", token, current); unicode = 0; for (int index = 0; index < 4; ++index) { Char c = *current++; unicode *= 16; if (c >= '0' && c <= '9') unicode += c - '0'; else if (c >= 'a' && c <= 'f') unicode += c - 'a' + 10; else if (c >= 'A' && c <= 'F') unicode += c - 'A' + 10; else return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current); } return true; } bool Reader::addError(const std::string& message, Token& token, Location extra) { ErrorInfo info; info.token_ = token; info.message_ = message; info.extra_ = extra; errors_.push_back(info); return false; } bool Reader::recoverFromError(TokenType skipUntilToken) { int errorCount = int(errors_.size()); Token skip; for (;;) { if (!readToken(skip)) errors_.resize(errorCount); // discard errors caused by recovery if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) break; } errors_.resize(errorCount); return false; } bool Reader::addErrorAndRecover(const std::string& message, Token& token, TokenType skipUntilToken) { addError(message, token); return recoverFromError(skipUntilToken); } Value& Reader::currentValue() { return *(nodes_.top()); } Reader::Char Reader::getNextChar() { if (current_ == end_) return 0; return *current_++; } void Reader::getLocationLineAndColumn(Location location, int& line, int& column) const { Location current = begin_; Location lastLineStart = current; line = 0; while (current < location && current != end_) { Char c = *current++; if (c == '\r') { if (*current == '\n') ++current; lastLineStart = current; ++line; } else if (c == '\n') { lastLineStart = current; ++line; } } // column & line start at 1 column = int(location - lastLineStart) + 1; ++line; } std::string Reader::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); char buffer[18 + 16 + 16 + 1]; snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); return buffer; } // Deprecated. Preserved for backward compatibility std::string Reader::getFormatedErrorMessages() const { return getFormattedErrorMessages(); } std::string Reader::getFormattedErrorMessages() const { std::string formattedMessage; for (Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError) { const ErrorInfo& error = *itError; formattedMessage += "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; formattedMessage += " " + error.message_ + "\n"; if (error.extra_) formattedMessage += "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; } return formattedMessage; } // Reader ///////////////////////// // exact copy of Features class OurFeatures { public: static OurFeatures all(); OurFeatures(); bool allowComments_; bool strictRoot_; bool allowDroppedNullPlaceholders_; bool allowNumericKeys_; bool allowSingleQuotes_; bool failIfExtra_; bool rejectDupKeys_; bool allowSpecialFloats_; int stackLimit_; }; // OurFeatures // exact copy of Implementation of class Features // //////////////////////////////// OurFeatures::OurFeatures() : allowComments_(true), strictRoot_(false) , allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) , allowSingleQuotes_(false) , failIfExtra_(false) , allowSpecialFloats_(false) { } OurFeatures OurFeatures::all() { return OurFeatures(); } // Implementation of class Reader // //////////////////////////////// // exact copy of Reader, renamed to OurReader class OurReader { public: typedef char Char; typedef const Char* Location; struct StructuredError { size_t offset_start; size_t offset_limit; std::string message; }; OurReader(OurFeatures const& features); bool parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true); std::string getFormattedErrorMessages() const; private: OurReader(OurReader const&); // no impl void operator=(OurReader const&); // no impl enum TokenType { tokenEndOfStream = 0, tokenObjectBegin, tokenObjectEnd, tokenArrayBegin, tokenArrayEnd, tokenString, tokenNumber, tokenTrue, tokenFalse, tokenNull, tokenNaN, tokenPosInf, tokenNegInf, tokenArraySeparator, tokenMemberSeparator, tokenComment, tokenError }; class Token { public: TokenType type_; Location start_; Location end_; }; class ErrorInfo { public: Token token_; std::string message_; Location extra_; }; typedef std::deque Errors; bool readToken(Token& token); void skipSpaces(); bool match(Location pattern, int patternLength); bool readComment(); bool readCStyleComment(); bool readCppStyleComment(); bool readString(); bool readStringSingleQuote(); bool readNumber(bool checkInf); bool readValue(); bool readObject(Token& token); bool readArray(Token& token); bool decodeNumber(Token& token); bool decodeNumber(Token& token, Value& decoded); bool decodeString(Token& token); bool decodeString(Token& token, std::string& decoded); bool decodeDouble(Token& token); bool decodeDouble(Token& token, Value& decoded); bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode); bool decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, unsigned int& unicode); bool addError(const std::string& message, Token& token, Location extra = 0); bool recoverFromError(TokenType skipUntilToken); bool addErrorAndRecover(const std::string& message, Token& token, TokenType skipUntilToken); void skipUntilSpace(); Value& currentValue(); Char getNextChar(); void getLocationLineAndColumn(Location location, int& line, int& column) const; std::string getLocationLineAndColumn(Location location) const; void addComment(Location begin, Location end, CommentPlacement placement); void skipCommentTokens(Token& token); typedef std::stack Nodes; Nodes nodes_; Errors errors_; std::string document_; Location begin_; Location end_; Location current_; Location lastValueEnd_; Value* lastValue_; std::string commentsBefore_; int stackDepth_; OurFeatures const features_; bool collectComments_; }; // OurReader // complete copy of Read impl, for OurReader OurReader::OurReader(OurFeatures const& features) : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), lastValue_(), commentsBefore_(), features_(features), collectComments_() { } bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments) { if (!features_.allowComments_) { collectComments = false; } begin_ = beginDoc; end_ = endDoc; collectComments_ = collectComments; current_ = begin_; lastValueEnd_ = 0; lastValue_ = 0; commentsBefore_ = ""; errors_.clear(); while (!nodes_.empty()) nodes_.pop(); nodes_.push(&root); stackDepth_ = 0; bool successful = readValue(); Token token; skipCommentTokens(token); if (features_.failIfExtra_) { if (token.type_ != tokenError && token.type_ != tokenEndOfStream) { addError("Extra non-whitespace after JSON value.", token); return false; } } if (collectComments_ && !commentsBefore_.empty()) root.setComment(commentsBefore_, commentAfter); if (features_.strictRoot_) { if (!root.isArray() && !root.isObject()) { // Set error location to start of doc, ideally should be first token found // in doc token.type_ = tokenError; token.start_ = beginDoc; token.end_ = endDoc; addError( "A valid JSON document must be either an array or an object value.", token); return false; } } return successful; } bool OurReader::readValue() { if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); ++stackDepth_; Token token; skipCommentTokens(token); bool successful = true; if (collectComments_ && !commentsBefore_.empty()) { currentValue().setComment(commentsBefore_, commentBefore); commentsBefore_ = ""; } switch (token.type_) { case tokenObjectBegin: successful = readObject(token); break; case tokenArrayBegin: successful = readArray(token); break; case tokenNumber: successful = decodeNumber(token); break; case tokenString: successful = decodeString(token); break; case tokenTrue: { Value v(true); currentValue().swapPayload(v); } break; case tokenFalse: { Value v(false); currentValue().swapPayload(v); } break; case tokenNull: { Value v; currentValue().swapPayload(v); } break; case tokenNaN: { Value v(std::numeric_limits::quiet_NaN()); currentValue().swapPayload(v); } break; case tokenPosInf: { Value v(std::numeric_limits::infinity()); currentValue().swapPayload(v); } break; case tokenNegInf: { Value v(-std::numeric_limits::infinity()); currentValue().swapPayload(v); } break; case tokenArraySeparator: case tokenObjectEnd: case tokenArrayEnd: if (features_.allowDroppedNullPlaceholders_) { // "Un-read" the current token and mark the current value as a null // token. current_--; Value v; currentValue().swapPayload(v); break; } // else, fall through ... default: return addError("Syntax error: value, object or array expected.", token); } if (collectComments_) { lastValueEnd_ = current_; lastValue_ = ¤tValue(); } --stackDepth_; return successful; } void OurReader::skipCommentTokens(Token& token) { if (features_.allowComments_) { do { readToken(token); } while (token.type_ == tokenComment); } else { readToken(token); } } bool OurReader::readToken(Token& token) { skipSpaces(); token.start_ = current_; Char c = getNextChar(); bool ok = true; switch (c) { case '{': token.type_ = tokenObjectBegin; break; case '}': token.type_ = tokenObjectEnd; break; case '[': token.type_ = tokenArrayBegin; break; case ']': token.type_ = tokenArrayEnd; break; case '"': token.type_ = tokenString; ok = readString(); break; case '\'': if (features_.allowSingleQuotes_) { token.type_ = tokenString; ok = readStringSingleQuote(); break; } // else continue case '/': token.type_ = tokenComment; ok = readComment(); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': token.type_ = tokenNumber; readNumber(false); break; case '-': if (readNumber(true)) { token.type_ = tokenNumber; } else { token.type_ = tokenNegInf; ok = features_.allowSpecialFloats_ && match("nfinity", 7); } break; case 't': token.type_ = tokenTrue; ok = match("rue", 3); break; case 'f': token.type_ = tokenFalse; ok = match("alse", 4); break; case 'n': token.type_ = tokenNull; ok = match("ull", 3); break; case 'N': if (features_.allowSpecialFloats_) { token.type_ = tokenNaN; ok = match("aN", 2); } else { ok = false; } break; case 'I': if (features_.allowSpecialFloats_) { token.type_ = tokenPosInf; ok = match("nfinity", 7); } else { ok = false; } break; case ',': token.type_ = tokenArraySeparator; break; case ':': token.type_ = tokenMemberSeparator; break; case 0: token.type_ = tokenEndOfStream; break; default: ok = false; break; } if (!ok) token.type_ = tokenError; token.end_ = current_; return true; } void OurReader::skipSpaces() { while (current_ != end_) { Char c = *current_; if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++current_; else break; } } bool OurReader::match(Location pattern, int patternLength) { if (end_ - current_ < patternLength) return false; int index = patternLength; while (index--) if (current_[index] != pattern[index]) return false; current_ += patternLength; return true; } bool OurReader::readComment() { Location commentBegin = current_ - 1; Char c = getNextChar(); bool successful = false; if (c == '*') successful = readCStyleComment(); else if (c == '/') successful = readCppStyleComment(); if (!successful) return false; if (collectComments_) { CommentPlacement placement = commentBefore; if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { if (c != '*' || !containsNewLine(commentBegin, current_)) placement = commentAfterOnSameLine; } addComment(commentBegin, current_, placement); } return true; } void OurReader::addComment(Location begin, Location end, CommentPlacement placement) { assert(collectComments_); const std::string& normalized = normalizeEOL(begin, end); if (placement == commentAfterOnSameLine) { assert(lastValue_ != 0); lastValue_->setComment(normalized, placement); } else { commentsBefore_ += normalized; } } bool OurReader::readCStyleComment() { while (current_ != end_) { Char c = getNextChar(); if (c == '*' && *current_ == '/') break; } return getNextChar() == '/'; } bool OurReader::readCppStyleComment() { while (current_ != end_) { Char c = getNextChar(); if (c == '\n') break; if (c == '\r') { // Consume DOS EOL. It will be normalized in addComment. if (current_ != end_ && *current_ == '\n') getNextChar(); // Break on Moc OS 9 EOL. break; } } return true; } bool OurReader::readNumber(bool checkInf) { const char *p = current_; if (checkInf && p != end_ && *p == 'I') { current_ = ++p; return false; } char c = '0'; // stopgap for already consumed character // integral part while (c >= '0' && c <= '9') c = (current_ = p) < end_ ? *p++ : 0; // fractional part if (c == '.') { c = (current_ = p) < end_ ? *p++ : 0; while (c >= '0' && c <= '9') c = (current_ = p) < end_ ? *p++ : 0; } // exponential part if (c == 'e' || c == 'E') { c = (current_ = p) < end_ ? *p++ : 0; if (c == '+' || c == '-') c = (current_ = p) < end_ ? *p++ : 0; while (c >= '0' && c <= '9') c = (current_ = p) < end_ ? *p++ : 0; } return true; } bool OurReader::readString() { Char c = 0; while (current_ != end_) { c = getNextChar(); if (c == '\\') getNextChar(); else if (c == '"') break; } return c == '"'; } bool OurReader::readStringSingleQuote() { Char c = 0; while (current_ != end_) { c = getNextChar(); if (c == '\\') getNextChar(); else if (c == '\'') break; } return c == '\''; } bool OurReader::readObject(Token& /*tokenStart*/) { Token tokenName; std::string name; Value init(objectValue); currentValue().swapPayload(init); while (readToken(tokenName)) { bool initialTokenOk = true; while (tokenName.type_ == tokenComment && initialTokenOk) initialTokenOk = readToken(tokenName); if (!initialTokenOk) break; if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object return true; name = ""; if (tokenName.type_ == tokenString) { if (!decodeString(tokenName, name)) return recoverFromError(tokenObjectEnd); } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { Value numberName; if (!decodeNumber(tokenName, numberName)) return recoverFromError(tokenObjectEnd); name = numberName.asString(); } else { break; } Token colon; if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { return addErrorAndRecover( "Missing ':' after object member name", colon, tokenObjectEnd); } if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30"); if (features_.rejectDupKeys_ && currentValue().isMember(name)) { std::string msg = "Duplicate key: '" + name + "'"; return addErrorAndRecover( msg, tokenName, tokenObjectEnd); } Value& value = currentValue()[name]; nodes_.push(&value); bool ok = readValue(); nodes_.pop(); if (!ok) // error already set return recoverFromError(tokenObjectEnd); Token comma; if (!readToken(comma) || (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && comma.type_ != tokenComment)) { return addErrorAndRecover( "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); } bool finalizeTokenOk = true; while (comma.type_ == tokenComment && finalizeTokenOk) finalizeTokenOk = readToken(comma); if (comma.type_ == tokenObjectEnd) return true; } return addErrorAndRecover( "Missing '}' or object member name", tokenName, tokenObjectEnd); } bool OurReader::readArray(Token& /*tokenStart*/) { Value init(arrayValue); currentValue().swapPayload(init); skipSpaces(); if (*current_ == ']') // empty array { Token endArray; readToken(endArray); return true; } int index = 0; for (;;) { Value& value = currentValue()[index++]; nodes_.push(&value); bool ok = readValue(); nodes_.pop(); if (!ok) // error already set return recoverFromError(tokenArrayEnd); Token token; // Accept Comment after last item in the array. ok = readToken(token); while (token.type_ == tokenComment && ok) { ok = readToken(token); } bool badTokenType = (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); if (!ok || badTokenType) { return addErrorAndRecover( "Missing ',' or ']' in array declaration", token, tokenArrayEnd); } if (token.type_ == tokenArrayEnd) break; } return true; } bool OurReader::decodeNumber(Token& token) { Value decoded; if (!decodeNumber(token, decoded)) return false; currentValue().swapPayload(decoded); return true; } bool OurReader::decodeNumber(Token& token, Value& decoded) { // Attempts to parse the number as an integer. If the number is // larger than the maximum supported value of an integer then // we decode the number as a double. Location current = token.start_; bool isNegative = *current == '-'; if (isNegative) ++current; // TODO: Help the compiler do the div and mod at compile time or get rid of them. Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) : Value::maxLargestUInt; Value::LargestUInt threshold = maxIntegerValue / 10; Value::LargestUInt value = 0; while (current < token.end_) { Char c = *current++; if (c < '0' || c > '9') return decodeDouble(token, decoded); Value::UInt digit(c - '0'); if (value >= threshold) { // We've hit or exceeded the max value divided by 10 (rounded down). If // a) we've only just touched the limit, b) this is the last digit, and // c) it's small enough to fit in that rounding delta, we're okay. // Otherwise treat this number as a double to avoid overflow. if (value > threshold || current != token.end_ || digit > maxIntegerValue % 10) { return decodeDouble(token, decoded); } } value = value * 10 + digit; } if (isNegative) decoded = -Value::LargestInt(value); else if (value <= Value::LargestUInt(Value::maxInt)) decoded = Value::LargestInt(value); else decoded = value; return true; } bool OurReader::decodeDouble(Token& token) { Value decoded; if (!decodeDouble(token, decoded)) return false; currentValue().swapPayload(decoded); return true; } bool OurReader::decodeDouble(Token& token, Value& decoded) { double value = 0; std::string buffer( token.start_, token.end_ ); std::istringstream is(buffer); if (!(is >> value)) return addError("'" + std::string(token.start_, token.end_) + "' is not a number.", token); decoded = value; return true; } bool OurReader::decodeString(Token& token) { std::string decoded_string; if (!decodeString(token, decoded_string)) return false; Value decoded(decoded_string); currentValue().swapPayload(decoded); return true; } bool OurReader::decodeString(Token& token, std::string& decoded) { decoded.reserve(token.end_ - token.start_ - 2); Location current = token.start_ + 1; // skip '"' Location end = token.end_ - 1; // do not include '"' while (current != end) { Char c = *current++; if (c == '"') break; else if (c == '\\') { if (current == end) return addError("Empty escape sequence in string", token, current); Char escape = *current++; switch (escape) { case '"': decoded += '"'; break; case '/': decoded += '/'; break; case '\\': decoded += '\\'; break; case 'b': decoded += '\b'; break; case 'f': decoded += '\f'; break; case 'n': decoded += '\n'; break; case 'r': decoded += '\r'; break; case 't': decoded += '\t'; break; case 'u': { unsigned int unicode; if (!decodeUnicodeCodePoint(token, current, end, unicode)) return false; decoded += codePointToUTF8(unicode); } break; default: return addError("Bad escape sequence in string", token, current); } } else { decoded += c; } } return true; } bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode) { if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) return false; if (unicode >= 0xD800 && unicode <= 0xDBFF) { // surrogate pairs if (end - current < 6) return addError( "additional six characters expected to parse unicode surrogate pair.", token, current); unsigned int surrogatePair; if (*(current++) == '\\' && *(current++) == 'u') { if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); } else return false; } else return addError("expecting another \\u token to begin the second half of " "a unicode surrogate pair", token, current); } return true; } bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, unsigned int& unicode) { if (end - current < 4) return addError( "Bad unicode escape sequence in string: four digits expected.", token, current); unicode = 0; for (int index = 0; index < 4; ++index) { Char c = *current++; unicode *= 16; if (c >= '0' && c <= '9') unicode += c - '0'; else if (c >= 'a' && c <= 'f') unicode += c - 'a' + 10; else if (c >= 'A' && c <= 'F') unicode += c - 'A' + 10; else return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current); } return true; } bool OurReader::addError(const std::string& message, Token& token, Location extra) { ErrorInfo info; info.token_ = token; info.message_ = message; info.extra_ = extra; errors_.push_back(info); return false; } bool OurReader::recoverFromError(TokenType skipUntilToken) { int errorCount = int(errors_.size()); Token skip; for (;;) { if (!readToken(skip)) errors_.resize(errorCount); // discard errors caused by recovery if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) break; } errors_.resize(errorCount); return false; } bool OurReader::addErrorAndRecover(const std::string& message, Token& token, TokenType skipUntilToken) { addError(message, token); return recoverFromError(skipUntilToken); } Value& OurReader::currentValue() { return *(nodes_.top()); } OurReader::Char OurReader::getNextChar() { if (current_ == end_) return 0; return *current_++; } void OurReader::getLocationLineAndColumn(Location location, int& line, int& column) const { Location current = begin_; Location lastLineStart = current; line = 0; while (current < location && current != end_) { Char c = *current++; if (c == '\r') { if (*current == '\n') ++current; lastLineStart = current; ++line; } else if (c == '\n') { lastLineStart = current; ++line; } } // column & line start at 1 column = int(location - lastLineStart) + 1; ++line; } std::string OurReader::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); char buffer[18 + 16 + 16 + 1]; snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); return buffer; } std::string OurReader::getFormattedErrorMessages() const { std::string formattedMessage; for (Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError) { const ErrorInfo& error = *itError; formattedMessage += "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; formattedMessage += " " + error.message_ + "\n"; if (error.extra_) formattedMessage += "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; } return formattedMessage; } class OurCharReader : public CharReader { bool const collectComments_; OurReader reader_; public: OurCharReader( bool collectComments, OurFeatures const& features) : collectComments_(collectComments) , reader_(features) {} virtual bool parse( char const* beginDoc, char const* endDoc, Value* root, std::string* errs) { bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); if (errs) { *errs = reader_.getFormattedErrorMessages(); } return ok; } }; CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); } CharReaderBuilder::~CharReaderBuilder() {} CharReader* CharReaderBuilder::newCharReader() const { bool collectComments = settings_["collectComments"].asBool(); OurFeatures features = OurFeatures::all(); features.allowComments_ = settings_["allowComments"].asBool(); features.strictRoot_ = settings_["strictRoot"].asBool(); features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); features.stackLimit_ = settings_["stackLimit"].asInt(); features.failIfExtra_ = settings_["failIfExtra"].asBool(); features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); return new OurCharReader(collectComments, features); } static void getValidReaderKeys(std::set* valid_keys) { valid_keys->clear(); valid_keys->insert("collectComments"); valid_keys->insert("allowComments"); valid_keys->insert("strictRoot"); valid_keys->insert("allowDroppedNullPlaceholders"); valid_keys->insert("allowNumericKeys"); valid_keys->insert("allowSingleQuotes"); valid_keys->insert("stackLimit"); valid_keys->insert("failIfExtra"); valid_keys->insert("rejectDupKeys"); valid_keys->insert("allowSpecialFloats"); } bool CharReaderBuilder::validate(Json::Value* invalid) const { Json::Value my_invalid; if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL Json::Value& inv = *invalid; std::set valid_keys; getValidReaderKeys(&valid_keys); Value::Members keys = settings_.getMemberNames(); size_t n = keys.size(); for (size_t i = 0; i < n; ++i) { std::string const& key = keys[i]; if (valid_keys.find(key) == valid_keys.end()) { inv[key] = settings_[key]; } } return 0u == inv.size(); } Value& CharReaderBuilder::operator[](std::string key) { return settings_[key]; } // static void CharReaderBuilder::strictMode(Json::Value* settings) { //! [CharReaderBuilderStrictMode] (*settings)["allowComments"] = false; (*settings)["strictRoot"] = true; (*settings)["allowDroppedNullPlaceholders"] = false; (*settings)["allowNumericKeys"] = false; (*settings)["allowSingleQuotes"] = false; (*settings)["failIfExtra"] = true; (*settings)["rejectDupKeys"] = true; (*settings)["allowSpecialFloats"] = false; //! [CharReaderBuilderStrictMode] } // static void CharReaderBuilder::setDefaults(Json::Value* settings) { //! [CharReaderBuilderDefaults] (*settings)["collectComments"] = true; (*settings)["allowComments"] = true; (*settings)["strictRoot"] = false; (*settings)["allowDroppedNullPlaceholders"] = false; (*settings)["allowNumericKeys"] = false; (*settings)["allowSingleQuotes"] = false; (*settings)["stackLimit"] = 1000; (*settings)["failIfExtra"] = false; (*settings)["rejectDupKeys"] = false; (*settings)["allowSpecialFloats"] = false; //! [CharReaderBuilderDefaults] } ////////////////////////////////// // global functions bool parseFromStream( CharReader::Factory const& fact, std::istream& sin, Value* root, std::string* errs) { std::ostringstream ssin; ssin << sin.rdbuf(); std::string doc = ssin.str(); char const* begin = doc.data(); char const* end = begin + doc.size(); // Note that we do not actually need a null-terminator. CharReaderPtr const reader(fact.newCharReader()); return reader->parse(begin, end, root, errs); } std::istream& operator>>(std::istream& sin, Value& root) { CharReaderBuilder b; std::string errs; bool ok = parseFromStream(b, sin, &root, &errs); if (!ok) { fprintf(stderr, "Error from reader: %s", errs.c_str()); throwRuntimeError("reader error"); } return sin; } } // namespace Json // ////////////////////////////////////////////////////////////////////// // End of content of file: src/lib_json/json_reader.cpp // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: src/lib_json/json_valueiterator.inl // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE // included by json_value.cpp namespace Json { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class ValueIteratorBase // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// ValueIteratorBase::ValueIteratorBase() : current_(), isNull_(true) { } ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator& current) : current_(current), isNull_(false) {} Value& ValueIteratorBase::deref() const { return current_->second; } void ValueIteratorBase::increment() { ++current_; } void ValueIteratorBase::decrement() { --current_; } ValueIteratorBase::difference_type ValueIteratorBase::computeDistance(const SelfType& other) const { #ifdef JSON_USE_CPPTL_SMALLMAP return other.current_ - current_; #else // Iterator for null value are initialized using the default // constructor, which initialize current_ to the default // std::map::iterator. As begin() and end() are two instance // of the default std::map::iterator, they can not be compared. // To allow this, we handle this comparison specifically. if (isNull_ && other.isNull_) { return 0; } // Usage of std::distance is not portable (does not compile with Sun Studio 12 // RogueWave STL, // which is the one used by default). // Using a portable hand-made version for non random iterator instead: // return difference_type( std::distance( current_, other.current_ ) ); difference_type myDistance = 0; for (Value::ObjectValues::iterator it = current_; it != other.current_; ++it) { ++myDistance; } return myDistance; #endif } bool ValueIteratorBase::isEqual(const SelfType& other) const { if (isNull_) { return other.isNull_; } return current_ == other.current_; } void ValueIteratorBase::copy(const SelfType& other) { current_ = other.current_; isNull_ = other.isNull_; } Value ValueIteratorBase::key() const { const Value::CZString czstring = (*current_).first; if (czstring.data()) { if (czstring.isStaticString()) return Value(StaticString(czstring.data())); return Value(czstring.data(), czstring.data() + czstring.length()); } return Value(czstring.index()); } UInt ValueIteratorBase::index() const { const Value::CZString czstring = (*current_).first; if (!czstring.data()) return czstring.index(); return Value::UInt(-1); } std::string ValueIteratorBase::name() const { char const* keey; char const* end; keey = memberName(&end); if (!keey) return std::string(); return std::string(keey, end); } char const* ValueIteratorBase::memberName() const { const char* cname = (*current_).first.data(); return cname ? cname : ""; } char const* ValueIteratorBase::memberName(char const** end) const { const char* cname = (*current_).first.data(); if (!cname) { *end = NULL; return NULL; } *end = cname + (*current_).first.length(); return cname; } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class ValueConstIterator // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// ValueConstIterator::ValueConstIterator() {} ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator& current) : ValueIteratorBase(current) {} ValueConstIterator& ValueConstIterator:: operator=(const ValueIteratorBase& other) { copy(other); return *this; } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class ValueIterator // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// ValueIterator::ValueIterator() {} ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) : ValueIteratorBase(current) {} ValueIterator::ValueIterator(const ValueConstIterator& other) : ValueIteratorBase(other) {} ValueIterator::ValueIterator(const ValueIterator& other) : ValueIteratorBase(other) {} ValueIterator& ValueIterator::operator=(const SelfType& other) { copy(other); return *this; } } // namespace Json // ////////////////////////////////////////////////////////////////////// // End of content of file: src/lib_json/json_valueiterator.inl // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: src/lib_json/json_value.cpp // ////////////////////////////////////////////////////////////////////// // Copyright 2011 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) #include #include #include #endif // if !defined(JSON_IS_AMALGAMATION) #include #include #include #include #include #ifdef JSON_USE_CPPTL #include #endif #include // size_t #include // min() #define JSON_ASSERT_UNREACHABLE assert(false) namespace Json { // This is a walkaround to avoid the static initialization of Value::null. // kNull must be word-aligned to avoid crashing on ARM. We use an alignment of // 8 (instead of 4) as a bit of future-proofing. #if defined(__ARMEL__) #define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) #else // This exists for binary compatibility only. Use nullRef. const Value Value::null; #define ALIGNAS(byte_alignment) #endif static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; const unsigned char& kNullRef = kNull[0]; const Value& Value::nullRef = reinterpret_cast(kNullRef); const Int Value::minInt = Int(~(UInt(-1) / 2)); const Int Value::maxInt = Int(UInt(-1) / 2); const UInt Value::maxUInt = UInt(-1); #if defined(JSON_HAS_INT64) const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); const UInt64 Value::maxUInt64 = UInt64(-1); // The constant is hard-coded because some compiler have trouble // converting Value::maxUInt64 to a double correctly (AIX/xlC). // Assumes that UInt64 is a 64 bits integer. static const double maxUInt64AsDouble = 18446744073709551615.0; #endif // defined(JSON_HAS_INT64) const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); const LargestUInt Value::maxLargestUInt = LargestUInt(-1); #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) template static inline bool InRange(double d, T min, U max) { return d >= min && d <= max; } #else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) static inline double integerToDouble(Json::UInt64 value) { return static_cast(Int64(value / 2)) * 2.0 + Int64(value & 1); } template static inline double integerToDouble(T value) { return static_cast(value); } template static inline bool InRange(double d, T min, U max) { return d >= integerToDouble(min) && d <= integerToDouble(max); } #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) /** Duplicates the specified string value. * @param value Pointer to the string to duplicate. Must be zero-terminated if * length is "unknown". * @param length Length of the value. if equals to unknown, then it will be * computed using strlen(value). * @return Pointer on the duplicate instance of string. */ static inline char* duplicateStringValue(const char* value, size_t length) { // Avoid an integer overflow in the call to malloc below by limiting length // to a sane value. if (length >= (size_t)Value::maxInt) length = Value::maxInt - 1; char* newString = static_cast(malloc(length + 1)); if (newString == NULL) { throwRuntimeError( "in Json::Value::duplicateStringValue(): " "Failed to allocate string value buffer"); } memcpy(newString, value, length); newString[length] = 0; return newString; } /* Record the length as a prefix. */ static inline char* duplicateAndPrefixStringValue( const char* value, unsigned int length) { // Avoid an integer overflow in the call to malloc below by limiting length // to a sane value. JSON_ASSERT_MESSAGE(length <= (unsigned)Value::maxInt - sizeof(unsigned) - 1U, "in Json::Value::duplicateAndPrefixStringValue(): " "length too big for prefixing"); unsigned actualLength = length + static_cast(sizeof(unsigned)) + 1U; char* newString = static_cast(malloc(actualLength)); if (newString == 0) { throwRuntimeError( "in Json::Value::duplicateAndPrefixStringValue(): " "Failed to allocate string value buffer"); } *reinterpret_cast(newString) = length; memcpy(newString + sizeof(unsigned), value, length); newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later return newString; } inline static void decodePrefixedString( bool isPrefixed, char const* prefixed, unsigned* length, char const** value) { if (!isPrefixed) { *length = static_cast(strlen(prefixed)); *value = prefixed; } else { *length = *reinterpret_cast(prefixed); *value = prefixed + sizeof(unsigned); } } /** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). */ static inline void releaseStringValue(char* value) { free(value); } } // namespace Json // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ValueInternals... // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// #if !defined(JSON_IS_AMALGAMATION) #include "json_valueiterator.inl" #endif // if !defined(JSON_IS_AMALGAMATION) namespace Json { Exception::Exception(std::string const& msg) : msg_(msg) {} Exception::~Exception() throw() {} char const* Exception::what() const throw() { return msg_.c_str(); } RuntimeError::RuntimeError(std::string const& msg) : Exception(msg) {} LogicError::LogicError(std::string const& msg) : Exception(msg) {} JSONCPP_NORETURN void throwRuntimeError(std::string const& msg) { throw RuntimeError(msg); } JSONCPP_NORETURN void throwLogicError(std::string const& msg) { throw LogicError(msg); } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class Value::CommentInfo // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// Value::CommentInfo::CommentInfo() : comment_(0) {} Value::CommentInfo::~CommentInfo() { if (comment_) releaseStringValue(comment_); } void Value::CommentInfo::setComment(const char* text, size_t len) { if (comment_) { releaseStringValue(comment_); comment_ = 0; } JSON_ASSERT(text != 0); JSON_ASSERT_MESSAGE( text[0] == '\0' || text[0] == '/', "in Json::Value::setComment(): Comments must start with /"); // It seems that /**/ style comments are acceptable as well. comment_ = duplicateStringValue(text, len); } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class Value::CZString // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // Notes: policy_ indicates if the string was allocated when // a string is stored. Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {} Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) : cstr_(str) { // allocate != duplicate storage_.policy_ = allocate & 0x3; storage_.length_ = ulength & 0x3FFFFFFF; } Value::CZString::CZString(const CZString& other) : cstr_(other.storage_.policy_ != noDuplication && other.cstr_ != 0 ? duplicateStringValue(other.cstr_, other.storage_.length_) : other.cstr_) { storage_.policy_ = (other.cstr_ ? (static_cast(other.storage_.policy_) == noDuplication ? noDuplication : duplicate) : static_cast(other.storage_.policy_)); storage_.length_ = other.storage_.length_; } Value::CZString::~CZString() { if (cstr_ && storage_.policy_ == duplicate) releaseStringValue(const_cast(cstr_)); } void Value::CZString::swap(CZString& other) { std::swap(cstr_, other.cstr_); std::swap(index_, other.index_); } Value::CZString& Value::CZString::operator=(CZString other) { swap(other); return *this; } bool Value::CZString::operator<(const CZString& other) const { if (!cstr_) return index_ < other.index_; //return strcmp(cstr_, other.cstr_) < 0; // Assume both are strings. unsigned this_len = this->storage_.length_; unsigned other_len = other.storage_.length_; unsigned min_len = std::min(this_len, other_len); int comp = memcmp(this->cstr_, other.cstr_, min_len); if (comp < 0) return true; if (comp > 0) return false; return (this_len < other_len); } bool Value::CZString::operator==(const CZString& other) const { if (!cstr_) return index_ == other.index_; //return strcmp(cstr_, other.cstr_) == 0; // Assume both are strings. unsigned this_len = this->storage_.length_; unsigned other_len = other.storage_.length_; if (this_len != other_len) return false; int comp = memcmp(this->cstr_, other.cstr_, this_len); return comp == 0; } ArrayIndex Value::CZString::index() const { return index_; } //const char* Value::CZString::c_str() const { return cstr_; } const char* Value::CZString::data() const { return cstr_; } unsigned Value::CZString::length() const { return storage_.length_; } bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class Value::Value // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// /*! \internal Default constructor initialization must be equivalent to: * memset( this, 0, sizeof(Value) ) * This optimization is used in ValueInternalMap fast allocator. */ Value::Value(ValueType vtype) { initBasic(vtype); switch (vtype) { case nullValue: break; case intValue: case uintValue: value_.int_ = 0; break; case realValue: value_.real_ = 0.0; break; case stringValue: value_.string_ = 0; break; case arrayValue: case objectValue: value_.map_ = new ObjectValues(); break; case booleanValue: value_.bool_ = false; break; default: JSON_ASSERT_UNREACHABLE; } } Value::Value(Int value) { initBasic(intValue); value_.int_ = value; } Value::Value(UInt value) { initBasic(uintValue); value_.uint_ = value; } #if defined(JSON_HAS_INT64) Value::Value(Int64 value) { initBasic(intValue); value_.int_ = value; } Value::Value(UInt64 value) { initBasic(uintValue); value_.uint_ = value; } #endif // defined(JSON_HAS_INT64) Value::Value(double value) { initBasic(realValue); value_.real_ = value; } Value::Value(const char* value) { initBasic(stringValue, true); value_.string_ = duplicateAndPrefixStringValue(value, static_cast(strlen(value))); } Value::Value(const char* beginValue, const char* endValue) { initBasic(stringValue, true); value_.string_ = duplicateAndPrefixStringValue(beginValue, static_cast(endValue - beginValue)); } Value::Value(const std::string& value) { initBasic(stringValue, true); value_.string_ = duplicateAndPrefixStringValue(value.data(), static_cast(value.length())); } Value::Value(const StaticString& value) { initBasic(stringValue); value_.string_ = const_cast(value.c_str()); } #ifdef JSON_USE_CPPTL Value::Value(const CppTL::ConstString& value) { initBasic(stringValue, true); value_.string_ = duplicateAndPrefixStringValue(value, static_cast(value.length())); } #endif Value::Value(bool value) { initBasic(booleanValue); value_.bool_ = value; } Value::Value(Value const& other) : type_(other.type_), allocated_(false) , comments_(0) { switch (type_) { case nullValue: case intValue: case uintValue: case realValue: case booleanValue: value_ = other.value_; break; case stringValue: if (other.value_.string_ && other.allocated_) { unsigned len; char const* str; decodePrefixedString(other.allocated_, other.value_.string_, &len, &str); value_.string_ = duplicateAndPrefixStringValue(str, len); allocated_ = true; } else { value_.string_ = other.value_.string_; allocated_ = false; } break; case arrayValue: case objectValue: value_.map_ = new ObjectValues(*other.value_.map_); break; default: JSON_ASSERT_UNREACHABLE; } if (other.comments_) { comments_ = new CommentInfo[numberOfCommentPlacement]; for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { const CommentInfo& otherComment = other.comments_[comment]; if (otherComment.comment_) comments_[comment].setComment( otherComment.comment_, strlen(otherComment.comment_)); } } } Value::~Value() { switch (type_) { case nullValue: case intValue: case uintValue: case realValue: case booleanValue: break; case stringValue: if (allocated_) releaseStringValue(value_.string_); break; case arrayValue: case objectValue: delete value_.map_; break; default: JSON_ASSERT_UNREACHABLE; } if (comments_) delete[] comments_; } Value &Value::operator=(const Value &other) { Value temp(other); swap(temp); return *this; } void Value::swapPayload(Value& other) { ValueType temp = type_; type_ = other.type_; other.type_ = temp; std::swap(value_, other.value_); int temp2 = allocated_; allocated_ = other.allocated_; other.allocated_ = temp2 & 0x1; } void Value::swap(Value& other) { swapPayload(other); std::swap(comments_, other.comments_); } ValueType Value::type() const { return type_; } int Value::compare(const Value& other) const { if (*this < other) return -1; if (*this > other) return 1; return 0; } bool Value::operator<(const Value& other) const { int typeDelta = type_ - other.type_; if (typeDelta) return typeDelta < 0 ? true : false; switch (type_) { case nullValue: return false; case intValue: return value_.int_ < other.value_.int_; case uintValue: return value_.uint_ < other.value_.uint_; case realValue: return value_.real_ < other.value_.real_; case booleanValue: return value_.bool_ < other.value_.bool_; case stringValue: { if ((value_.string_ == 0) || (other.value_.string_ == 0)) { if (other.value_.string_) return true; else return false; } unsigned this_len; unsigned other_len; char const* this_str; char const* other_str; decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); unsigned min_len = std::min(this_len, other_len); int comp = memcmp(this_str, other_str, min_len); if (comp < 0) return true; if (comp > 0) return false; return (this_len < other_len); } case arrayValue: case objectValue: { int delta = int(value_.map_->size() - other.value_.map_->size()); if (delta) return delta < 0; return (*value_.map_) < (*other.value_.map_); } default: JSON_ASSERT_UNREACHABLE; } return false; // unreachable } bool Value::operator<=(const Value& other) const { return !(other < *this); } bool Value::operator>=(const Value& other) const { return !(*this < other); } bool Value::operator>(const Value& other) const { return other < *this; } bool Value::operator==(const Value& other) const { // if ( type_ != other.type_ ) // GCC 2.95.3 says: // attempt to take address of bit-field structure member `Json::Value::type_' // Beats me, but a temp solves the problem. int temp = other.type_; if (type_ != temp) return false; switch (type_) { case nullValue: return true; case intValue: return value_.int_ == other.value_.int_; case uintValue: return value_.uint_ == other.value_.uint_; case realValue: return value_.real_ == other.value_.real_; case booleanValue: return value_.bool_ == other.value_.bool_; case stringValue: { if ((value_.string_ == 0) || (other.value_.string_ == 0)) { return (value_.string_ == other.value_.string_); } unsigned this_len; unsigned other_len; char const* this_str; char const* other_str; decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); if (this_len != other_len) return false; int comp = memcmp(this_str, other_str, this_len); return comp == 0; } case arrayValue: case objectValue: return value_.map_->size() == other.value_.map_->size() && (*value_.map_) == (*other.value_.map_); default: JSON_ASSERT_UNREACHABLE; } return false; // unreachable } bool Value::operator!=(const Value& other) const { return !(*this == other); } const char* Value::asCString() const { JSON_ASSERT_MESSAGE(type_ == stringValue, "in Json::Value::asCString(): requires stringValue"); if (value_.string_ == 0) return 0; unsigned this_len; char const* this_str; decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); return this_str; } bool Value::getString(char const** str, char const** cend) const { if (type_ != stringValue) return false; if (value_.string_ == 0) return false; unsigned length; decodePrefixedString(this->allocated_, this->value_.string_, &length, str); *cend = *str + length; return true; } std::string Value::asString() const { switch (type_) { case nullValue: return ""; case stringValue: { if (value_.string_ == 0) return ""; unsigned this_len; char const* this_str; decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); return std::string(this_str, this_len); } case booleanValue: return value_.bool_ ? "true" : "false"; case intValue: return valueToString(value_.int_); case uintValue: return valueToString(value_.uint_); case realValue: return valueToString(value_.real_); default: JSON_FAIL_MESSAGE("Type is not convertible to string"); } } #ifdef JSON_USE_CPPTL CppTL::ConstString Value::asConstString() const { unsigned len; char const* str; decodePrefixedString(allocated_, value_.string_, &len, &str); return CppTL::ConstString(str, len); } #endif Value::Int Value::asInt() const { switch (type_) { case intValue: JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); return Int(value_.int_); case uintValue: JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); return Int(value_.uint_); case realValue: JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), "double out of Int range"); return Int(value_.real_); case nullValue: return 0; case booleanValue: return value_.bool_ ? 1 : 0; default: break; } JSON_FAIL_MESSAGE("Value is not convertible to Int."); } Value::UInt Value::asUInt() const { switch (type_) { case intValue: JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); return UInt(value_.int_); case uintValue: JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); return UInt(value_.uint_); case realValue: JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), "double out of UInt range"); return UInt(value_.real_); case nullValue: return 0; case booleanValue: return value_.bool_ ? 1 : 0; default: break; } JSON_FAIL_MESSAGE("Value is not convertible to UInt."); } #if defined(JSON_HAS_INT64) Value::Int64 Value::asInt64() const { switch (type_) { case intValue: return Int64(value_.int_); case uintValue: JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); return Int64(value_.uint_); case realValue: JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), "double out of Int64 range"); return Int64(value_.real_); case nullValue: return 0; case booleanValue: return value_.bool_ ? 1 : 0; default: break; } JSON_FAIL_MESSAGE("Value is not convertible to Int64."); } Value::UInt64 Value::asUInt64() const { switch (type_) { case intValue: JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); return UInt64(value_.int_); case uintValue: return UInt64(value_.uint_); case realValue: JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), "double out of UInt64 range"); return UInt64(value_.real_); case nullValue: return 0; case booleanValue: return value_.bool_ ? 1 : 0; default: break; } JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); } #endif // if defined(JSON_HAS_INT64) LargestInt Value::asLargestInt() const { #if defined(JSON_NO_INT64) return asInt(); #else return asInt64(); #endif } LargestUInt Value::asLargestUInt() const { #if defined(JSON_NO_INT64) return asUInt(); #else return asUInt64(); #endif } double Value::asDouble() const { switch (type_) { case intValue: return static_cast(value_.int_); case uintValue: #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) return static_cast(value_.uint_); #else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) return integerToDouble(value_.uint_); #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) case realValue: return value_.real_; case nullValue: return 0.0; case booleanValue: return value_.bool_ ? 1.0 : 0.0; default: break; } JSON_FAIL_MESSAGE("Value is not convertible to double."); } float Value::asFloat() const { switch (type_) { case intValue: return static_cast(value_.int_); case uintValue: #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) return static_cast(value_.uint_); #else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) return integerToDouble(value_.uint_); #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) case realValue: return static_cast(value_.real_); case nullValue: return 0.0; case booleanValue: return value_.bool_ ? 1.0f : 0.0f; default: break; } JSON_FAIL_MESSAGE("Value is not convertible to float."); } bool Value::asBool() const { switch (type_) { case booleanValue: return value_.bool_; case nullValue: return false; case intValue: return value_.int_ ? true : false; case uintValue: return value_.uint_ ? true : false; case realValue: // This is kind of strange. Not recommended. return (value_.real_ != 0.0) ? true : false; default: break; } JSON_FAIL_MESSAGE("Value is not convertible to bool."); } bool Value::isConvertibleTo(ValueType other) const { switch (other) { case nullValue: return (isNumeric() && asDouble() == 0.0) || (type_ == booleanValue && value_.bool_ == false) || (type_ == stringValue && asString() == "") || (type_ == arrayValue && value_.map_->size() == 0) || (type_ == objectValue && value_.map_->size() == 0) || type_ == nullValue; case intValue: return isInt() || (type_ == realValue && InRange(value_.real_, minInt, maxInt)) || type_ == booleanValue || type_ == nullValue; case uintValue: return isUInt() || (type_ == realValue && InRange(value_.real_, 0, maxUInt)) || type_ == booleanValue || type_ == nullValue; case realValue: return isNumeric() || type_ == booleanValue || type_ == nullValue; case booleanValue: return isNumeric() || type_ == booleanValue || type_ == nullValue; case stringValue: return isNumeric() || type_ == booleanValue || type_ == stringValue || type_ == nullValue; case arrayValue: return type_ == arrayValue || type_ == nullValue; case objectValue: return type_ == objectValue || type_ == nullValue; } JSON_ASSERT_UNREACHABLE; return false; } /// Number of values in array or object ArrayIndex Value::size() const { switch (type_) { case nullValue: case intValue: case uintValue: case realValue: case booleanValue: case stringValue: return 0; case arrayValue: // size of the array is highest index + 1 if (!value_.map_->empty()) { ObjectValues::const_iterator itLast = value_.map_->end(); --itLast; return (*itLast).first.index() + 1; } return 0; case objectValue: return ArrayIndex(value_.map_->size()); } JSON_ASSERT_UNREACHABLE; return 0; // unreachable; } bool Value::empty() const { if (isNull() || isArray() || isObject()) return size() == 0u; else return false; } bool Value::operator!() const { return isNull(); } void Value::clear() { JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || type_ == objectValue, "in Json::Value::clear(): requires complex value"); switch (type_) { case arrayValue: case objectValue: value_.map_->clear(); break; default: break; } } void Value::resize(ArrayIndex newSize) { JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, "in Json::Value::resize(): requires arrayValue"); if (type_ == nullValue) *this = Value(arrayValue); ArrayIndex oldSize = size(); if (newSize == 0) clear(); else if (newSize > oldSize) (*this)[newSize - 1]; else { for (ArrayIndex index = newSize; index < oldSize; ++index) { value_.map_->erase(index); } assert(size() == newSize); } } Value& Value::operator[](ArrayIndex index) { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == arrayValue, "in Json::Value::operator[](ArrayIndex): requires arrayValue"); if (type_ == nullValue) *this = Value(arrayValue); CZString key(index); ObjectValues::iterator it = value_.map_->lower_bound(key); if (it != value_.map_->end() && (*it).first == key) return (*it).second; ObjectValues::value_type defaultValue(key, nullRef); it = value_.map_->insert(it, defaultValue); return (*it).second; } Value& Value::operator[](int index) { JSON_ASSERT_MESSAGE( index >= 0, "in Json::Value::operator[](int index): index cannot be negative"); return (*this)[ArrayIndex(index)]; } const Value& Value::operator[](ArrayIndex index) const { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == arrayValue, "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); if (type_ == nullValue) return nullRef; CZString key(index); ObjectValues::const_iterator it = value_.map_->find(key); if (it == value_.map_->end()) return nullRef; return (*it).second; } const Value& Value::operator[](int index) const { JSON_ASSERT_MESSAGE( index >= 0, "in Json::Value::operator[](int index) const: index cannot be negative"); return (*this)[ArrayIndex(index)]; } void Value::initBasic(ValueType vtype, bool allocated) { type_ = vtype; allocated_ = allocated; comments_ = 0; } // Access an object value by name, create a null member if it does not exist. // @pre Type of '*this' is object or null. // @param key is null-terminated. Value& Value::resolveReference(const char* key) { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, "in Json::Value::resolveReference(): requires objectValue"); if (type_ == nullValue) *this = Value(objectValue); CZString actualKey( key, static_cast(strlen(key)), CZString::noDuplication); // NOTE! ObjectValues::iterator it = value_.map_->lower_bound(actualKey); if (it != value_.map_->end() && (*it).first == actualKey) return (*it).second; ObjectValues::value_type defaultValue(actualKey, nullRef); it = value_.map_->insert(it, defaultValue); Value& value = (*it).second; return value; } // @param key is not null-terminated. Value& Value::resolveReference(char const* key, char const* cend) { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, "in Json::Value::resolveReference(key, end): requires objectValue"); if (type_ == nullValue) *this = Value(objectValue); CZString actualKey( key, static_cast(cend-key), CZString::duplicateOnCopy); ObjectValues::iterator it = value_.map_->lower_bound(actualKey); if (it != value_.map_->end() && (*it).first == actualKey) return (*it).second; ObjectValues::value_type defaultValue(actualKey, nullRef); it = value_.map_->insert(it, defaultValue); Value& value = (*it).second; return value; } Value Value::get(ArrayIndex index, const Value& defaultValue) const { const Value* value = &((*this)[index]); return value == &nullRef ? defaultValue : *value; } bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } Value const* Value::find(char const* key, char const* cend) const { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, "in Json::Value::find(key, end, found): requires objectValue or nullValue"); if (type_ == nullValue) return NULL; CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); ObjectValues::const_iterator it = value_.map_->find(actualKey); if (it == value_.map_->end()) return NULL; return &(*it).second; } const Value& Value::operator[](const char* key) const { Value const* found = find(key, key + strlen(key)); if (!found) return nullRef; return *found; } Value const& Value::operator[](std::string const& key) const { Value const* found = find(key.data(), key.data() + key.length()); if (!found) return nullRef; return *found; } Value& Value::operator[](const char* key) { return resolveReference(key, key + strlen(key)); } Value& Value::operator[](const std::string& key) { return resolveReference(key.data(), key.data() + key.length()); } Value& Value::operator[](const StaticString& key) { return resolveReference(key.c_str()); } #ifdef JSON_USE_CPPTL Value& Value::operator[](const CppTL::ConstString& key) { return resolveReference(key.c_str(), key.end_c_str()); } Value const& Value::operator[](CppTL::ConstString const& key) const { Value const* found = find(key.c_str(), key.end_c_str()); if (!found) return nullRef; return *found; } #endif Value& Value::append(const Value& value) { return (*this)[size()] = value; } Value Value::get(char const* key, char const* cend, Value const& defaultValue) const { Value const* found = find(key, cend); return !found ? defaultValue : *found; } Value Value::get(char const* key, Value const& defaultValue) const { return get(key, key + strlen(key), defaultValue); } Value Value::get(std::string const& key, Value const& defaultValue) const { return get(key.data(), key.data() + key.length(), defaultValue); } bool Value::removeMember(const char* key, const char* cend, Value* removed) { if (type_ != objectValue) { return false; } CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); ObjectValues::iterator it = value_.map_->find(actualKey); if (it == value_.map_->end()) return false; *removed = it->second; value_.map_->erase(it); return true; } bool Value::removeMember(const char* key, Value* removed) { return removeMember(key, key + strlen(key), removed); } bool Value::removeMember(std::string const& key, Value* removed) { return removeMember(key.data(), key.data() + key.length(), removed); } Value Value::removeMember(const char* key) { JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, "in Json::Value::removeMember(): requires objectValue"); if (type_ == nullValue) return nullRef; Value removed; // null removeMember(key, key + strlen(key), &removed); return removed; // still null if removeMember() did nothing } Value Value::removeMember(const std::string& key) { return removeMember(key.c_str()); } bool Value::removeIndex(ArrayIndex index, Value* removed) { if (type_ != arrayValue) { return false; } CZString key(index); ObjectValues::iterator it = value_.map_->find(key); if (it == value_.map_->end()) { return false; } *removed = it->second; ArrayIndex oldSize = size(); // shift left all items left, into the place of the "removed" for (ArrayIndex i = index; i < (oldSize - 1); ++i){ CZString keey(i); (*value_.map_)[keey] = (*this)[i + 1]; } // erase the last one ("leftover") CZString keyLast(oldSize - 1); ObjectValues::iterator itLast = value_.map_->find(keyLast); value_.map_->erase(itLast); return true; } #ifdef JSON_USE_CPPTL Value Value::get(const CppTL::ConstString& key, const Value& defaultValue) const { return get(key.c_str(), key.end_c_str(), defaultValue); } #endif bool Value::isMember(char const* key, char const* cend) const { Value const* value = find(key, cend); return NULL != value; } bool Value::isMember(char const* key) const { return isMember(key, key + strlen(key)); } bool Value::isMember(std::string const& key) const { return isMember(key.data(), key.data() + key.length()); } #ifdef JSON_USE_CPPTL bool Value::isMember(const CppTL::ConstString& key) const { return isMember(key.c_str(), key.end_c_str()); } #endif Value::Members Value::getMemberNames() const { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, "in Json::Value::getMemberNames(), value must be objectValue"); if (type_ == nullValue) return Value::Members(); Members members; members.reserve(value_.map_->size()); ObjectValues::const_iterator it = value_.map_->begin(); ObjectValues::const_iterator itEnd = value_.map_->end(); for (; it != itEnd; ++it) { members.push_back(std::string((*it).first.data(), (*it).first.length())); } return members; } // //# ifdef JSON_USE_CPPTL // EnumMemberNames // Value::enumMemberNames() const //{ // if ( type_ == objectValue ) // { // return CppTL::Enum::any( CppTL::Enum::transform( // CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), // MemberNamesTransform() ) ); // } // return EnumMemberNames(); //} // // // EnumValues // Value::enumValues() const //{ // if ( type_ == objectValue || type_ == arrayValue ) // return CppTL::Enum::anyValues( *(value_.map_), // CppTL::Type() ); // return EnumValues(); //} // //# endif static bool IsIntegral(double d) { double integral_part; return modf(d, &integral_part) == 0.0; } bool Value::isNull() const { return type_ == nullValue; } bool Value::isBool() const { return type_ == booleanValue; } bool Value::isInt() const { switch (type_) { case intValue: return value_.int_ >= minInt && value_.int_ <= maxInt; case uintValue: return value_.uint_ <= UInt(maxInt); case realValue: return value_.real_ >= minInt && value_.real_ <= maxInt && IsIntegral(value_.real_); default: break; } return false; } bool Value::isUInt() const { switch (type_) { case intValue: return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); case uintValue: return value_.uint_ <= maxUInt; case realValue: return value_.real_ >= 0 && value_.real_ <= maxUInt && IsIntegral(value_.real_); default: break; } return false; } bool Value::isInt64() const { #if defined(JSON_HAS_INT64) switch (type_) { case intValue: return true; case uintValue: return value_.uint_ <= UInt64(maxInt64); case realValue: // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a // double, so double(maxInt64) will be rounded up to 2^63. Therefore we // require the value to be strictly less than the limit. return value_.real_ >= double(minInt64) && value_.real_ < double(maxInt64) && IsIntegral(value_.real_); default: break; } #endif // JSON_HAS_INT64 return false; } bool Value::isUInt64() const { #if defined(JSON_HAS_INT64) switch (type_) { case intValue: return value_.int_ >= 0; case uintValue: return true; case realValue: // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we // require the value to be strictly less than the limit. return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_); default: break; } #endif // JSON_HAS_INT64 return false; } bool Value::isIntegral() const { #if defined(JSON_HAS_INT64) return isInt64() || isUInt64(); #else return isInt() || isUInt(); #endif } bool Value::isDouble() const { return type_ == realValue || isIntegral(); } bool Value::isNumeric() const { return isIntegral() || isDouble(); } bool Value::isString() const { return type_ == stringValue; } bool Value::isArray() const { return type_ == arrayValue; } bool Value::isObject() const { return type_ == objectValue; } void Value::setComment(const char* comment, size_t len, CommentPlacement placement) { if (!comments_) comments_ = new CommentInfo[numberOfCommentPlacement]; if ((len > 0) && (comment[len-1] == '\n')) { // Always discard trailing newline, to aid indentation. len -= 1; } comments_[placement].setComment(comment, len); } void Value::setComment(const char* comment, CommentPlacement placement) { setComment(comment, strlen(comment), placement); } void Value::setComment(const std::string& comment, CommentPlacement placement) { setComment(comment.c_str(), comment.length(), placement); } bool Value::hasComment(CommentPlacement placement) const { return comments_ != 0 && comments_[placement].comment_ != 0; } std::string Value::getComment(CommentPlacement placement) const { if (hasComment(placement)) return comments_[placement].comment_; return ""; } std::string Value::toStyledString() const { StyledWriter writer; return writer.write(*this); } Value::const_iterator Value::begin() const { switch (type_) { case arrayValue: case objectValue: if (value_.map_) return const_iterator(value_.map_->begin()); break; default: break; } return const_iterator(); } Value::const_iterator Value::end() const { switch (type_) { case arrayValue: case objectValue: if (value_.map_) return const_iterator(value_.map_->end()); break; default: break; } return const_iterator(); } Value::iterator Value::begin() { switch (type_) { case arrayValue: case objectValue: if (value_.map_) return iterator(value_.map_->begin()); break; default: break; } return iterator(); } Value::iterator Value::end() { switch (type_) { case arrayValue: case objectValue: if (value_.map_) return iterator(value_.map_->end()); break; default: break; } return iterator(); } // class PathArgument // ////////////////////////////////////////////////////////////////// PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} PathArgument::PathArgument(ArrayIndex index) : key_(), index_(index), kind_(kindIndex) {} PathArgument::PathArgument(const char* key) : key_(key), index_(), kind_(kindKey) {} PathArgument::PathArgument(const std::string& key) : key_(key.c_str()), index_(), kind_(kindKey) {} // class Path // ////////////////////////////////////////////////////////////////// Path::Path(const std::string& path, const PathArgument& a1, const PathArgument& a2, const PathArgument& a3, const PathArgument& a4, const PathArgument& a5) { InArgs in; in.push_back(&a1); in.push_back(&a2); in.push_back(&a3); in.push_back(&a4); in.push_back(&a5); makePath(path, in); } void Path::makePath(const std::string& path, const InArgs& in) { const char* current = path.c_str(); const char* end = current + path.length(); InArgs::const_iterator itInArg = in.begin(); while (current != end) { if (*current == '[') { ++current; if (*current == '%') addPathInArg(path, in, itInArg, PathArgument::kindIndex); else { ArrayIndex index = 0; for (; current != end && *current >= '0' && *current <= '9'; ++current) index = index * 10 + ArrayIndex(*current - '0'); args_.push_back(index); } if (current == end || *current++ != ']') invalidPath(path, int(current - path.c_str())); } else if (*current == '%') { addPathInArg(path, in, itInArg, PathArgument::kindKey); ++current; } else if (*current == '.') { ++current; } else { const char* beginName = current; while (current != end && !strchr("[.", *current)) ++current; args_.push_back(std::string(beginName, current)); } } } void Path::addPathInArg(const std::string& /*path*/, const InArgs& in, InArgs::const_iterator& itInArg, PathArgument::Kind kind) { if (itInArg == in.end()) { // Error: missing argument %d } else if ((*itInArg)->kind_ != kind) { // Error: bad argument type } else { args_.push_back(**itInArg); } } void Path::invalidPath(const std::string& /*path*/, int /*location*/) { // Error: invalid path. } const Value& Path::resolve(const Value& root) const { const Value* node = &root; for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { const PathArgument& arg = *it; if (arg.kind_ == PathArgument::kindIndex) { if (!node->isArray() || !node->isValidIndex(arg.index_)) { // Error: unable to resolve path (array value expected at position... } node = &((*node)[arg.index_]); } else if (arg.kind_ == PathArgument::kindKey) { if (!node->isObject()) { // Error: unable to resolve path (object value expected at position...) } node = &((*node)[arg.key_]); if (node == &Value::nullRef) { // Error: unable to resolve path (object has no member named '' at // position...) } } } return *node; } Value Path::resolve(const Value& root, const Value& defaultValue) const { const Value* node = &root; for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { const PathArgument& arg = *it; if (arg.kind_ == PathArgument::kindIndex) { if (!node->isArray() || !node->isValidIndex(arg.index_)) return defaultValue; node = &((*node)[arg.index_]); } else if (arg.kind_ == PathArgument::kindKey) { if (!node->isObject()) return defaultValue; node = &((*node)[arg.key_]); if (node == &Value::nullRef) return defaultValue; } } return *node; } Value& Path::make(Value& root) const { Value* node = &root; for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { const PathArgument& arg = *it; if (arg.kind_ == PathArgument::kindIndex) { if (!node->isArray()) { // Error: node is not an array at position ... } node = &((*node)[arg.index_]); } else if (arg.kind_ == PathArgument::kindKey) { if (!node->isObject()) { // Error: node is not an object at position... } node = &((*node)[arg.key_]); } } return *node; } } // namespace Json // ////////////////////////////////////////////////////////////////////// // End of content of file: src/lib_json/json_value.cpp // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: src/lib_json/json_writer.cpp // ////////////////////////////////////////////////////////////////////// // Copyright 2011 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) #include #include "json_tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include #include #include #include #include #include #include #if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 #include #define isfinite _finite #elif defined(__sun) && defined(__SVR4) //Solaris #include #define isfinite finite #else #include #define isfinite std::isfinite #endif #if defined(_MSC_VER) #if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above #define snprintf sprintf_s #elif _MSC_VER >= 1900 // VC++ 14.0 and above #define snprintf std::snprintf #else #define snprintf _snprintf #endif #elif defined(__ANDROID__) #define snprintf snprintf #elif __cplusplus >= 201103L #define snprintf std::snprintf #endif #if defined(__BORLANDC__) #include #define isfinite _finite #define snprintf _snprintf #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 // Disable warning about strdup being deprecated. #pragma warning(disable : 4996) #endif namespace Json { #if __cplusplus >= 201103L typedef std::unique_ptr const StreamWriterPtr; #else typedef std::auto_ptr StreamWriterPtr; #endif static bool containsControlCharacter(const char* str) { while (*str) { if (isControlCharacter(*(str++))) return true; } return false; } static bool containsControlCharacter0(const char* str, unsigned len) { char const* end = str + len; while (end != str) { if (isControlCharacter(*str) || 0==*str) return true; ++str; } return false; } std::string valueToString(LargestInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); if (value == Value::minLargestInt) { uintToString(LargestUInt(Value::maxLargestInt) + 1, current); *--current = '-'; } else if (value < 0) { uintToString(LargestUInt(-value), current); *--current = '-'; } else { uintToString(LargestUInt(value), current); } assert(current >= buffer); return current; } std::string valueToString(LargestUInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); uintToString(value, current); assert(current >= buffer); return current; } #if defined(JSON_HAS_INT64) std::string valueToString(Int value) { return valueToString(LargestInt(value)); } std::string valueToString(UInt value) { return valueToString(LargestUInt(value)); } #endif // # if defined(JSON_HAS_INT64) std::string valueToString(double value, bool useSpecialFloats, unsigned int precision) { // Allocate a buffer that is more than large enough to store the 16 digits of // precision requested below. char buffer[32]; int len = -1; char formatString[6]; sprintf(formatString, "%%.%dg", precision); // Print into the buffer. We need not request the alternative representation // that always has a decimal point because JSON doesn't distingish the // concepts of reals and integers. if (isfinite(value)) { len = snprintf(buffer, sizeof(buffer), formatString, value); } else { // IEEE standard states that NaN values will not compare to themselves if (value != value) { len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null"); } else if (value < 0) { len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999"); } else { len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999"); } // For those, we do not need to call fixNumLoc, but it is fast. } assert(len >= 0); fixNumericLocale(buffer, buffer + len); return buffer; } std::string valueToString(double value) { return valueToString(value, false, 17); } std::string valueToString(bool value) { return value ? "true" : "false"; } std::string valueToQuotedString(const char* value) { if (value == NULL) return ""; // Not sure how to handle unicode... if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter(value)) return std::string("\"") + value + "\""; // We have to walk value and escape any special characters. // Appending to std::string is not efficient, but this should be rare. // (Note: forward slashes are *not* rare, but I am not escaping them.) std::string::size_type maxsize = strlen(value) * 2 + 3; // allescaped+quotes+NULL std::string result; result.reserve(maxsize); // to avoid lots of mallocs result += "\""; for (const char* c = value; *c != 0; ++c) { switch (*c) { case '\"': result += "\\\""; break; case '\\': result += "\\\\"; break; case '\b': result += "\\b"; break; case '\f': result += "\\f"; break; case '\n': result += "\\n"; break; case '\r': result += "\\r"; break; case '\t': result += "\\t"; break; // case '/': // Even though \/ is considered a legal escape in JSON, a bare // slash is also legal, so I see no reason to escape it. // (I hope I am not misunderstanding something. // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); result += oss.str(); } else { result += *c; } break; } } result += "\""; return result; } // https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp static char const* strnpbrk(char const* s, char const* accept, size_t n) { assert((s || !n) && accept); char const* const end = s + n; for (char const* cur = s; cur < end; ++cur) { int const c = *cur; for (char const* a = accept; *a; ++a) { if (*a == c) { return cur; } } } return NULL; } static std::string valueToQuotedStringN(const char* value, unsigned length) { if (value == NULL) return ""; // Not sure how to handle unicode... if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL && !containsControlCharacter0(value, length)) return std::string("\"") + value + "\""; // We have to walk value and escape any special characters. // Appending to std::string is not efficient, but this should be rare. // (Note: forward slashes are *not* rare, but I am not escaping them.) std::string::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL std::string result; result.reserve(maxsize); // to avoid lots of mallocs result += "\""; char const* end = value + length; for (const char* c = value; c != end; ++c) { switch (*c) { case '\"': result += "\\\""; break; case '\\': result += "\\\\"; break; case '\b': result += "\\b"; break; case '\f': result += "\\f"; break; case '\n': result += "\\n"; break; case '\r': result += "\\r"; break; case '\t': result += "\\t"; break; // case '/': // Even though \/ is considered a legal escape in JSON, a bare // slash is also legal, so I see no reason to escape it. // (I hope I am not misunderstanding something.) // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); result += oss.str(); } else { result += *c; } break; } } result += "\""; return result; } // Class Writer // ////////////////////////////////////////////////////////////////// Writer::~Writer() {} // Class FastWriter // ////////////////////////////////////////////////////////////////// FastWriter::FastWriter() : yamlCompatiblityEnabled_(false) {} void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } std::string FastWriter::write(const Value& root) { document_ = ""; writeValue(root); document_ += "\n"; return document_; } void FastWriter::writeValue(const Value& value) { switch (value.type()) { case nullValue: document_ += "null"; break; case intValue: document_ += valueToString(value.asLargestInt()); break; case uintValue: document_ += valueToString(value.asLargestUInt()); break; case realValue: document_ += valueToString(value.asDouble()); break; case stringValue: { // Is NULL possible for value.string_? char const* str; char const* end; bool ok = value.getString(&str, &end); if (ok) document_ += valueToQuotedStringN(str, static_cast(end-str)); break; } case booleanValue: document_ += valueToString(value.asBool()); break; case arrayValue: { document_ += '['; int size = value.size(); for (int index = 0; index < size; ++index) { if (index > 0) document_ += ','; writeValue(value[index]); } document_ += ']'; } break; case objectValue: { Value::Members members(value.getMemberNames()); document_ += '{'; for (Value::Members::iterator it = members.begin(); it != members.end(); ++it) { const std::string& name = *it; if (it != members.begin()) document_ += ','; document_ += valueToQuotedStringN(name.data(), static_cast(name.length())); document_ += yamlCompatiblityEnabled_ ? ": " : ":"; writeValue(value[name]); } document_ += '}'; } break; } } // Class StyledWriter // ////////////////////////////////////////////////////////////////// StyledWriter::StyledWriter() : rightMargin_(74), indentSize_(3), addChildValues_() {} std::string StyledWriter::write(const Value& root) { document_ = ""; addChildValues_ = false; indentString_ = ""; writeCommentBeforeValue(root); writeValue(root); writeCommentAfterValueOnSameLine(root); document_ += "\n"; return document_; } void StyledWriter::writeValue(const Value& value) { switch (value.type()) { case nullValue: pushValue("null"); break; case intValue: pushValue(valueToString(value.asLargestInt())); break; case uintValue: pushValue(valueToString(value.asLargestUInt())); break; case realValue: pushValue(valueToString(value.asDouble())); break; case stringValue: { // Is NULL possible for value.string_? char const* str; char const* end; bool ok = value.getString(&str, &end); if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); else pushValue(""); break; } case booleanValue: pushValue(valueToString(value.asBool())); break; case arrayValue: writeArrayValue(value); break; case objectValue: { Value::Members members(value.getMemberNames()); if (members.empty()) pushValue("{}"); else { writeWithIndent("{"); indent(); Value::Members::iterator it = members.begin(); for (;;) { const std::string& name = *it; const Value& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); document_ += " : "; writeValue(childValue); if (++it == members.end()) { writeCommentAfterValueOnSameLine(childValue); break; } document_ += ','; writeCommentAfterValueOnSameLine(childValue); } unindent(); writeWithIndent("}"); } } break; } } void StyledWriter::writeArrayValue(const Value& value) { unsigned size = value.size(); if (size == 0) pushValue("[]"); else { bool isArrayMultiLine = isMultineArray(value); if (isArrayMultiLine) { writeWithIndent("["); indent(); bool hasChildValue = !childValues_.empty(); unsigned index = 0; for (;;) { const Value& childValue = value[index]; writeCommentBeforeValue(childValue); if (hasChildValue) writeWithIndent(childValues_[index]); else { writeIndent(); writeValue(childValue); } if (++index == size) { writeCommentAfterValueOnSameLine(childValue); break; } document_ += ','; writeCommentAfterValueOnSameLine(childValue); } unindent(); writeWithIndent("]"); } else // output on a single line { assert(childValues_.size() == size); document_ += "[ "; for (unsigned index = 0; index < size; ++index) { if (index > 0) document_ += ", "; document_ += childValues_[index]; } document_ += " ]"; } } } bool StyledWriter::isMultineArray(const Value& value) { int size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); for (int index = 0; index < size && !isMultiLine; ++index) { const Value& childValue = value[index]; isMultiLine = isMultiLine || ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); } if (!isMultiLine) // check if line length > max line length { childValues_.reserve(size); addChildValues_ = true; int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' for (int index = 0; index < size; ++index) { if (hasCommentForValue(value[index])) { isMultiLine = true; } writeValue(value[index]); lineLength += int(childValues_[index].length()); } addChildValues_ = false; isMultiLine = isMultiLine || lineLength >= rightMargin_; } return isMultiLine; } void StyledWriter::pushValue(const std::string& value) { if (addChildValues_) childValues_.push_back(value); else document_ += value; } void StyledWriter::writeIndent() { if (!document_.empty()) { char last = document_[document_.length() - 1]; if (last == ' ') // already indented return; if (last != '\n') // Comments may add new-line document_ += '\n'; } document_ += indentString_; } void StyledWriter::writeWithIndent(const std::string& value) { writeIndent(); document_ += value; } void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); } void StyledWriter::unindent() { assert(int(indentString_.size()) >= indentSize_); indentString_.resize(indentString_.size() - indentSize_); } void StyledWriter::writeCommentBeforeValue(const Value& root) { if (!root.hasComment(commentBefore)) return; document_ += "\n"; writeIndent(); const std::string& comment = root.getComment(commentBefore); std::string::const_iterator iter = comment.begin(); while (iter != comment.end()) { document_ += *iter; if (*iter == '\n' && (iter != comment.end() && *(iter + 1) == '/')) writeIndent(); ++iter; } // Comments are stripped of trailing newlines, so add one here document_ += "\n"; } void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { if (root.hasComment(commentAfterOnSameLine)) document_ += " " + root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { document_ += "\n"; document_ += root.getComment(commentAfter); document_ += "\n"; } } bool StyledWriter::hasCommentForValue(const Value& value) { return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || value.hasComment(commentAfter); } // Class StyledStreamWriter // ////////////////////////////////////////////////////////////////// StyledStreamWriter::StyledStreamWriter(std::string indentation) : document_(NULL), rightMargin_(74), indentation_(indentation), addChildValues_() {} void StyledStreamWriter::write(std::ostream& out, const Value& root) { document_ = &out; addChildValues_ = false; indentString_ = ""; indented_ = true; writeCommentBeforeValue(root); if (!indented_) writeIndent(); indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); *document_ << "\n"; document_ = NULL; // Forget the stream, for safety. } void StyledStreamWriter::writeValue(const Value& value) { switch (value.type()) { case nullValue: pushValue("null"); break; case intValue: pushValue(valueToString(value.asLargestInt())); break; case uintValue: pushValue(valueToString(value.asLargestUInt())); break; case realValue: pushValue(valueToString(value.asDouble())); break; case stringValue: { // Is NULL possible for value.string_? char const* str; char const* end; bool ok = value.getString(&str, &end); if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); else pushValue(""); break; } case booleanValue: pushValue(valueToString(value.asBool())); break; case arrayValue: writeArrayValue(value); break; case objectValue: { Value::Members members(value.getMemberNames()); if (members.empty()) pushValue("{}"); else { writeWithIndent("{"); indent(); Value::Members::iterator it = members.begin(); for (;;) { const std::string& name = *it; const Value& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); *document_ << " : "; writeValue(childValue); if (++it == members.end()) { writeCommentAfterValueOnSameLine(childValue); break; } *document_ << ","; writeCommentAfterValueOnSameLine(childValue); } unindent(); writeWithIndent("}"); } } break; } } void StyledStreamWriter::writeArrayValue(const Value& value) { unsigned size = value.size(); if (size == 0) pushValue("[]"); else { bool isArrayMultiLine = isMultineArray(value); if (isArrayMultiLine) { writeWithIndent("["); indent(); bool hasChildValue = !childValues_.empty(); unsigned index = 0; for (;;) { const Value& childValue = value[index]; writeCommentBeforeValue(childValue); if (hasChildValue) writeWithIndent(childValues_[index]); else { if (!indented_) writeIndent(); indented_ = true; writeValue(childValue); indented_ = false; } if (++index == size) { writeCommentAfterValueOnSameLine(childValue); break; } *document_ << ","; writeCommentAfterValueOnSameLine(childValue); } unindent(); writeWithIndent("]"); } else // output on a single line { assert(childValues_.size() == size); *document_ << "[ "; for (unsigned index = 0; index < size; ++index) { if (index > 0) *document_ << ", "; *document_ << childValues_[index]; } *document_ << " ]"; } } } bool StyledStreamWriter::isMultineArray(const Value& value) { int size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); for (int index = 0; index < size && !isMultiLine; ++index) { const Value& childValue = value[index]; isMultiLine = isMultiLine || ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); } if (!isMultiLine) // check if line length > max line length { childValues_.reserve(size); addChildValues_ = true; int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' for (int index = 0; index < size; ++index) { if (hasCommentForValue(value[index])) { isMultiLine = true; } writeValue(value[index]); lineLength += int(childValues_[index].length()); } addChildValues_ = false; isMultiLine = isMultiLine || lineLength >= rightMargin_; } return isMultiLine; } void StyledStreamWriter::pushValue(const std::string& value) { if (addChildValues_) childValues_.push_back(value); else *document_ << value; } void StyledStreamWriter::writeIndent() { // blep intended this to look at the so-far-written string // to determine whether we are already indented, but // with a stream we cannot do that. So we rely on some saved state. // The caller checks indented_. *document_ << '\n' << indentString_; } void StyledStreamWriter::writeWithIndent(const std::string& value) { if (!indented_) writeIndent(); *document_ << value; indented_ = false; } void StyledStreamWriter::indent() { indentString_ += indentation_; } void StyledStreamWriter::unindent() { assert(indentString_.size() >= indentation_.size()); indentString_.resize(indentString_.size() - indentation_.size()); } void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { if (!root.hasComment(commentBefore)) return; if (!indented_) writeIndent(); const std::string& comment = root.getComment(commentBefore); std::string::const_iterator iter = comment.begin(); while (iter != comment.end()) { *document_ << *iter; if (*iter == '\n' && (iter != comment.end() && *(iter + 1) == '/')) // writeIndent(); // would include newline *document_ << indentString_; ++iter; } indented_ = false; } void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { if (root.hasComment(commentAfterOnSameLine)) *document_ << ' ' << root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { writeIndent(); *document_ << root.getComment(commentAfter); } indented_ = false; } bool StyledStreamWriter::hasCommentForValue(const Value& value) { return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || value.hasComment(commentAfter); } ////////////////////////// // BuiltStyledStreamWriter /// Scoped enums are not available until C++11. struct CommentStyle { /// Decide whether to write comments. enum Enum { None, ///< Drop all comments. Most, ///< Recover odd behavior of previous versions (not implemented yet). All ///< Keep all comments. }; }; struct BuiltStyledStreamWriter : public StreamWriter { BuiltStyledStreamWriter( std::string const& indentation, CommentStyle::Enum cs, std::string const& colonSymbol, std::string const& nullSymbol, std::string const& endingLineFeedSymbol, bool useSpecialFloats, unsigned int precision); virtual int write(Value const& root, std::ostream* sout); private: void writeValue(Value const& value); void writeArrayValue(Value const& value); bool isMultineArray(Value const& value); void pushValue(std::string const& value); void writeIndent(); void writeWithIndent(std::string const& value); void indent(); void unindent(); void writeCommentBeforeValue(Value const& root); void writeCommentAfterValueOnSameLine(Value const& root); static bool hasCommentForValue(const Value& value); typedef std::vector ChildValues; ChildValues childValues_; std::string indentString_; int rightMargin_; std::string indentation_; CommentStyle::Enum cs_; std::string colonSymbol_; std::string nullSymbol_; std::string endingLineFeedSymbol_; bool addChildValues_ : 1; bool indented_ : 1; bool useSpecialFloats_ : 1; unsigned int precision_; }; BuiltStyledStreamWriter::BuiltStyledStreamWriter( std::string const& indentation, CommentStyle::Enum cs, std::string const& colonSymbol, std::string const& nullSymbol, std::string const& endingLineFeedSymbol, bool useSpecialFloats, unsigned int precision) : rightMargin_(74) , indentation_(indentation) , cs_(cs) , colonSymbol_(colonSymbol) , nullSymbol_(nullSymbol) , endingLineFeedSymbol_(endingLineFeedSymbol) , addChildValues_(false) , indented_(false) , useSpecialFloats_(useSpecialFloats) , precision_(precision) { } int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout) { sout_ = sout; addChildValues_ = false; indented_ = true; indentString_ = ""; writeCommentBeforeValue(root); if (!indented_) writeIndent(); indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); *sout_ << endingLineFeedSymbol_; sout_ = NULL; return 0; } void BuiltStyledStreamWriter::writeValue(Value const& value) { switch (value.type()) { case nullValue: pushValue(nullSymbol_); break; case intValue: pushValue(valueToString(value.asLargestInt())); break; case uintValue: pushValue(valueToString(value.asLargestUInt())); break; case realValue: pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_)); break; case stringValue: { // Is NULL is possible for value.string_? char const* str; char const* end; bool ok = value.getString(&str, &end); if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); else pushValue(""); break; } case booleanValue: pushValue(valueToString(value.asBool())); break; case arrayValue: writeArrayValue(value); break; case objectValue: { Value::Members members(value.getMemberNames()); if (members.empty()) pushValue("{}"); else { writeWithIndent("{"); indent(); Value::Members::iterator it = members.begin(); for (;;) { std::string const& name = *it; Value const& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedStringN(name.data(), static_cast(name.length()))); *sout_ << colonSymbol_; writeValue(childValue); if (++it == members.end()) { writeCommentAfterValueOnSameLine(childValue); break; } *sout_ << ","; writeCommentAfterValueOnSameLine(childValue); } unindent(); writeWithIndent("}"); } } break; } } void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { unsigned size = value.size(); if (size == 0) pushValue("[]"); else { bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value); if (isMultiLine) { writeWithIndent("["); indent(); bool hasChildValue = !childValues_.empty(); unsigned index = 0; for (;;) { Value const& childValue = value[index]; writeCommentBeforeValue(childValue); if (hasChildValue) writeWithIndent(childValues_[index]); else { if (!indented_) writeIndent(); indented_ = true; writeValue(childValue); indented_ = false; } if (++index == size) { writeCommentAfterValueOnSameLine(childValue); break; } *sout_ << ","; writeCommentAfterValueOnSameLine(childValue); } unindent(); writeWithIndent("]"); } else // output on a single line { assert(childValues_.size() == size); *sout_ << "["; if (!indentation_.empty()) *sout_ << " "; for (unsigned index = 0; index < size; ++index) { if (index > 0) *sout_ << ", "; *sout_ << childValues_[index]; } if (!indentation_.empty()) *sout_ << " "; *sout_ << "]"; } } } bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { int size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); for (int index = 0; index < size && !isMultiLine; ++index) { Value const& childValue = value[index]; isMultiLine = isMultiLine || ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0); } if (!isMultiLine) // check if line length > max line length { childValues_.reserve(size); addChildValues_ = true; int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' for (int index = 0; index < size; ++index) { if (hasCommentForValue(value[index])) { isMultiLine = true; } writeValue(value[index]); lineLength += int(childValues_[index].length()); } addChildValues_ = false; isMultiLine = isMultiLine || lineLength >= rightMargin_; } return isMultiLine; } void BuiltStyledStreamWriter::pushValue(std::string const& value) { if (addChildValues_) childValues_.push_back(value); else *sout_ << value; } void BuiltStyledStreamWriter::writeIndent() { // blep intended this to look at the so-far-written string // to determine whether we are already indented, but // with a stream we cannot do that. So we rely on some saved state. // The caller checks indented_. if (!indentation_.empty()) { // In this case, drop newlines too. *sout_ << '\n' << indentString_; } } void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) { if (!indented_) writeIndent(); *sout_ << value; indented_ = false; } void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } void BuiltStyledStreamWriter::unindent() { assert(indentString_.size() >= indentation_.size()); indentString_.resize(indentString_.size() - indentation_.size()); } void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { if (cs_ == CommentStyle::None) return; if (!root.hasComment(commentBefore)) return; if (!indented_) writeIndent(); const std::string& comment = root.getComment(commentBefore); std::string::const_iterator iter = comment.begin(); while (iter != comment.end()) { *sout_ << *iter; if (*iter == '\n' && (iter != comment.end() && *(iter + 1) == '/')) // writeIndent(); // would write extra newline *sout_ << indentString_; ++iter; } indented_ = false; } void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { if (cs_ == CommentStyle::None) return; if (root.hasComment(commentAfterOnSameLine)) *sout_ << " " + root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { writeIndent(); *sout_ << root.getComment(commentAfter); } } // static bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || value.hasComment(commentAfter); } /////////////// // StreamWriter StreamWriter::StreamWriter() : sout_(NULL) { } StreamWriter::~StreamWriter() { } StreamWriter::Factory::~Factory() {} StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); } StreamWriterBuilder::~StreamWriterBuilder() {} StreamWriter* StreamWriterBuilder::newStreamWriter() const { std::string indentation = settings_["indentation"].asString(); std::string cs_str = settings_["commentStyle"].asString(); bool eyc = settings_["enableYAMLCompatibility"].asBool(); bool dnp = settings_["dropNullPlaceholders"].asBool(); bool usf = settings_["useSpecialFloats"].asBool(); unsigned int pre = settings_["precision"].asUInt(); CommentStyle::Enum cs = CommentStyle::All; if (cs_str == "All") { cs = CommentStyle::All; } else if (cs_str == "None") { cs = CommentStyle::None; } else { throwRuntimeError("commentStyle must be 'All' or 'None'"); } std::string colonSymbol = " : "; if (eyc) { colonSymbol = ": "; } else if (indentation.empty()) { colonSymbol = ":"; } std::string nullSymbol = "null"; if (dnp) { nullSymbol = ""; } if (pre > 17) pre = 17; std::string endingLineFeedSymbol = ""; return new BuiltStyledStreamWriter( indentation, cs, colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); } static void getValidWriterKeys(std::set* valid_keys) { valid_keys->clear(); valid_keys->insert("indentation"); valid_keys->insert("commentStyle"); valid_keys->insert("enableYAMLCompatibility"); valid_keys->insert("dropNullPlaceholders"); valid_keys->insert("useSpecialFloats"); valid_keys->insert("precision"); } bool StreamWriterBuilder::validate(Json::Value* invalid) const { Json::Value my_invalid; if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL Json::Value& inv = *invalid; std::set valid_keys; getValidWriterKeys(&valid_keys); Value::Members keys = settings_.getMemberNames(); size_t n = keys.size(); for (size_t i = 0; i < n; ++i) { std::string const& key = keys[i]; if (valid_keys.find(key) == valid_keys.end()) { inv[key] = settings_[key]; } } return 0u == inv.size(); } Value& StreamWriterBuilder::operator[](std::string key) { return settings_[key]; } // static void StreamWriterBuilder::setDefaults(Json::Value* settings) { //! [StreamWriterBuilderDefaults] (*settings)["commentStyle"] = "All"; (*settings)["indentation"] = "\t"; (*settings)["enableYAMLCompatibility"] = false; (*settings)["dropNullPlaceholders"] = false; (*settings)["useSpecialFloats"] = false; (*settings)["precision"] = 17; //! [StreamWriterBuilderDefaults] } std::string writeString(StreamWriter::Factory const& builder, Value const& root) { std::ostringstream sout; StreamWriterPtr const writer(builder.newStreamWriter()); writer->write(root, &sout); return sout.str(); } std::ostream& operator<<(std::ostream& sout, Value const& root) { StreamWriterBuilder builder; StreamWriterPtr const writer(builder.newStreamWriter()); writer->write(root, &sout); return sout; } } // namespace Json // ////////////////////////////////////////////////////////////////////// // End of content of file: src/lib_json/json_writer.cpp // ////////////////////////////////////////////////////////////////////// sysdig-0.26.4/userspace/libsinsp/third-party/tinydir.h000066400000000000000000000206671352731327100230430ustar00rootroot00000000000000/* Copyright (c) 2013-2014, Cong Xu All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TINYDIR_H #define TINYDIR_H #include #include #include #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN #include #pragma warning (disable : 4996) #else #include #include #endif /* types */ #define _TINYDIR_PATH_MAX 4096 #ifdef _MSC_VER /* extra chars for the "\\*" mask */ #define _TINYDIR_PATH_EXTRA 2 #else #define _TINYDIR_PATH_EXTRA 0 #endif #define _TINYDIR_FILENAME_MAX 256 #ifdef _MSC_VER #define strncasecmp _strnicmp #else #include #endif #ifdef _MSC_VER #define _TINYDIR_FUNC static __inline #else #define _TINYDIR_FUNC static __inline__ #endif typedef struct { char path[_TINYDIR_PATH_MAX]; char name[_TINYDIR_FILENAME_MAX]; int is_dir; int is_reg; #ifdef _MSC_VER #else struct stat _s; #endif } tinydir_file; typedef struct { char path[_TINYDIR_PATH_MAX]; int has_next; size_t n_files; tinydir_file *_files; #ifdef _MSC_VER HANDLE _h; WIN32_FIND_DATA _f; #else DIR *_d; struct dirent *_e; #endif } tinydir_dir; /* declarations */ _TINYDIR_FUNC int tinydir_open(tinydir_dir *dir, const char *path); _TINYDIR_FUNC int tinydir_open_sorted(tinydir_dir *dir, const char *path); _TINYDIR_FUNC void tinydir_close(tinydir_dir *dir); _TINYDIR_FUNC int tinydir_next(tinydir_dir *dir); _TINYDIR_FUNC int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file); _TINYDIR_FUNC int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i); _TINYDIR_FUNC int tinydir_open_subdir_n(tinydir_dir *dir, size_t i); _TINYDIR_FUNC int _tinydir_file_cmp(const void *a, const void *b); /* definitions*/ _TINYDIR_FUNC int tinydir_open(tinydir_dir *dir, const char *path) { if (dir == NULL || path == NULL || strlen(path) == 0) { errno = EINVAL; return -1; } if (strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) { errno = ENAMETOOLONG; return -1; } /* initialise dir */ dir->_files = NULL; #ifdef _MSC_VER dir->_h = INVALID_HANDLE_VALUE; #else dir->_d = NULL; #endif tinydir_close(dir); strcpy(dir->path, path); #ifdef _MSC_VER strcat(dir->path, "\\*"); dir->_h = FindFirstFile(dir->path, &dir->_f); dir->path[strlen(dir->path) - 2] = '\0'; if (dir->_h == INVALID_HANDLE_VALUE) #else dir->_d = opendir(path); if (dir->_d == NULL) #endif { errno = ENOENT; goto bail; } /* read first file */ dir->has_next = 1; #ifndef _MSC_VER dir->_e = readdir(dir->_d); if (dir->_e == NULL) { dir->has_next = 0; } #endif return 0; bail: tinydir_close(dir); return -1; } _TINYDIR_FUNC int tinydir_open_sorted(tinydir_dir *dir, const char *path) { /* Count the number of files first, to pre-allocate the files array */ size_t n_files = 0; if (tinydir_open(dir, path) == -1) { return -1; } while (dir->has_next) { n_files++; if (tinydir_next(dir) == -1) { goto bail; } } tinydir_close(dir); if (tinydir_open(dir, path) == -1) { return -1; } dir->n_files = 0; dir->_files = (tinydir_file *)malloc(sizeof *dir->_files * n_files); if (dir->_files == NULL) { errno = ENOMEM; goto bail; } while (dir->has_next) { tinydir_file *p_file; dir->n_files++; p_file = &dir->_files[dir->n_files - 1]; if (tinydir_readfile(dir, p_file) == -1) { goto bail; } if (tinydir_next(dir) == -1) { goto bail; } /* Just in case the number of files has changed between the first and second reads, terminate without writing into unallocated memory */ if (dir->n_files == n_files) { break; } } qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp); return 0; bail: tinydir_close(dir); return -1; } _TINYDIR_FUNC void tinydir_close(tinydir_dir *dir) { if (dir == NULL) { return; } memset(dir->path, 0, sizeof(dir->path)); dir->has_next = 0; dir->n_files = 0; if (dir->_files != NULL) { free(dir->_files); } dir->_files = NULL; #ifdef _MSC_VER if (dir->_h != INVALID_HANDLE_VALUE) { FindClose(dir->_h); } dir->_h = INVALID_HANDLE_VALUE; #else if (dir->_d) { closedir(dir->_d); } dir->_d = NULL; dir->_e = NULL; #endif } _TINYDIR_FUNC int tinydir_next(tinydir_dir *dir) { if (dir == NULL) { errno = EINVAL; return -1; } if (!dir->has_next) { errno = ENOENT; return -1; } #ifdef _MSC_VER if (FindNextFile(dir->_h, &dir->_f) == 0) #else dir->_e = readdir(dir->_d); if (dir->_e == NULL) #endif { dir->has_next = 0; #ifdef _MSC_VER if (GetLastError() != ERROR_SUCCESS && GetLastError() != ERROR_NO_MORE_FILES) { tinydir_close(dir); errno = EIO; return -1; } #endif } return 0; } _TINYDIR_FUNC int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file) { if (dir == NULL || file == NULL) { errno = EINVAL; return -1; } #ifdef _MSC_VER if (dir->_h == INVALID_HANDLE_VALUE) #else if (dir->_e == NULL) #endif { errno = ENOENT; return -1; } if (strlen(dir->path) + strlen( #ifdef _MSC_VER dir->_f.cFileName #else dir->_e->d_name #endif ) + 1 + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) { /* the path for the file will be too long */ errno = ENAMETOOLONG; return -1; } if (strlen( #ifdef _MSC_VER dir->_f.cFileName #else dir->_e->d_name #endif ) >= _TINYDIR_FILENAME_MAX) { errno = ENAMETOOLONG; return -1; } strcpy(file->path, dir->path); strcat(file->path, "/"); strcpy(file->name, #ifdef _MSC_VER dir->_f.cFileName #else dir->_e->d_name #endif ); strcat(file->path, file->name); #ifndef _MSC_VER if (stat(file->path, &file->_s) == -1) { return -1; } #endif file->is_dir = #ifdef _MSC_VER !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); #else S_ISDIR(file->_s.st_mode); #endif file->is_reg = #ifdef _MSC_VER !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || ( !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) && !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) && #ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) && #endif #ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) && #endif !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) && !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY)); #else S_ISREG(file->_s.st_mode); #endif return 0; } _TINYDIR_FUNC int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i) { if (dir == NULL || file == NULL) { errno = EINVAL; return -1; } if (i >= dir->n_files) { errno = ENOENT; return -1; } memcpy(file, &dir->_files[i], sizeof(tinydir_file)); return 0; } _TINYDIR_FUNC int tinydir_open_subdir_n(tinydir_dir *dir, size_t i) { char path[_TINYDIR_PATH_MAX]; if (dir == NULL) { errno = EINVAL; return -1; } if (i >= dir->n_files || !dir->_files[i].is_dir) { errno = ENOENT; return -1; } strcpy(path, dir->_files[i].path); tinydir_close(dir); if (tinydir_open_sorted(dir, path) == -1) { return -1; } return 0; } _TINYDIR_FUNC int _tinydir_file_cmp(const void *a, const void *b) { const tinydir_file *fa = (const tinydir_file *)a; const tinydir_file *fb = (const tinydir_file *)b; if (fa->is_dir != fb->is_dir) { return -(fa->is_dir - fb->is_dir); } return strncasecmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX); } #endif sysdig-0.26.4/userspace/libsinsp/threadinfo.cpp000066400000000000000000001226451352731327100215670ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #ifndef _WIN32 #define __STDC_FORMAT_MACROS #include #endif #include #include "sinsp.h" #include "sinsp_int.h" #include "protodecoder.h" #include "tracers.h" extern sinsp_evttables g_infotables; static void copy_ipv6_address(uint32_t* dest, uint32_t* src) { dest[0] = src[0]; dest[1] = src[1]; dest[2] = src[2]; dest[3] = src[3]; } /////////////////////////////////////////////////////////////////////////////// // sinsp_threadinfo implementation /////////////////////////////////////////////////////////////////////////////// sinsp_threadinfo::sinsp_threadinfo() : m_fdtable(NULL) { m_inspector = NULL; m_tracer_parser = NULL; init(); } sinsp_threadinfo::sinsp_threadinfo(sinsp *inspector) : m_fdtable(inspector) { m_inspector = inspector; m_tracer_parser = NULL; init(); } void sinsp_threadinfo::init() { m_pid = (uint64_t) - 1LL; m_sid = (uint64_t) - 1LL; m_vpgid = (uint64_t) - 1LL; set_lastevent_data_validity(false); m_lastevent_type = -1; m_lastevent_ts = 0; m_prevevent_ts = 0; m_lastaccess_ts = 0; m_clone_ts = 0; m_lastevent_category.m_category = EC_UNKNOWN; m_flags = PPM_CL_NAME_CHANGED; m_nchilds = 0; m_fdlimit = -1; m_vmsize_kb = 0; m_vmrss_kb = 0; m_vmswap_kb = 0; m_pfmajor = 0; m_pfminor = 0; m_vtid = -1; m_vpid = -1; m_main_thread.reset(); m_lastevent_fd = 0; #ifdef HAS_FILTERING m_last_latency_entertime = 0; m_latency = 0; #endif m_ainfo = NULL; m_program_hash = 0; m_program_hash_falco = 0; m_lastevent_data = NULL; m_parent_loop_detected = false; m_tty = 0; m_category = CAT_NONE; m_blprogram = NULL; m_loginuid = 0; } sinsp_threadinfo::~sinsp_threadinfo() { uint32_t j; if((m_inspector != NULL) && (m_inspector->m_thread_manager != NULL) && (m_inspector->m_thread_manager->m_listener != NULL)) { m_inspector->m_thread_manager->m_listener->on_thread_destroyed(this); } for(j = 0; j < m_private_state.size(); j++) { free(m_private_state[j]); } m_private_state.clear(); if(m_lastevent_data) { free(m_lastevent_data); } if(m_tracer_parser) { delete m_tracer_parser; } } void sinsp_threadinfo::fix_sockets_coming_from_proc() { unordered_map::iterator it; for(it = m_fdtable.m_table.begin(); it != m_fdtable.m_table.end(); it++) { if(it->second.m_type == SCAP_FD_IPV4_SOCK) { if(m_inspector->m_thread_manager->m_server_ports.find(it->second.m_sockinfo.m_ipv4info.m_fields.m_sport) != m_inspector->m_thread_manager->m_server_ports.end()) { uint32_t tip; uint16_t tport; tip = it->second.m_sockinfo.m_ipv4info.m_fields.m_sip; tport = it->second.m_sockinfo.m_ipv4info.m_fields.m_sport; it->second.m_sockinfo.m_ipv4info.m_fields.m_sip = it->second.m_sockinfo.m_ipv4info.m_fields.m_dip; it->second.m_sockinfo.m_ipv4info.m_fields.m_dip = tip; it->second.m_sockinfo.m_ipv4info.m_fields.m_sport = it->second.m_sockinfo.m_ipv4info.m_fields.m_dport; it->second.m_sockinfo.m_ipv4info.m_fields.m_dport = tport; it->second.m_name = ipv4tuple_to_string(&it->second.m_sockinfo.m_ipv4info, m_inspector->m_hostname_and_port_resolution_enabled); it->second.set_role_server(); } else { it->second.set_role_client(); } } } } #define STR_AS_NUM_JAVA 0x6176616a #define STR_AS_NUM_RUBY 0x79627572 #define STR_AS_NUM_PERL 0x6c726570 #define STR_AS_NUM_NODE 0x65646f6e #define MAX_PROG_HASH_LEN 1024 void sinsp_threadinfo::compute_program_hash() { auto curr_hash = std::hash()(m_exe); hash_combine(curr_hash, m_container_id); auto rem_len = MAX_PROG_HASH_LEN - (m_exe.size() + m_container_id.size()); // // By default, the falco hash is just exe+container // m_program_hash_falco = curr_hash; // // The program hash includes the arguments as well // for (auto arg = m_args.begin(); arg != m_args.end() && rem_len > 0; ++arg) { if (arg->size() >= rem_len) { auto partial_str = arg->substr(0, rem_len); hash_combine(curr_hash, partial_str); break; } hash_combine(curr_hash, *arg); rem_len -= arg->size(); } m_program_hash = curr_hash; // // For some specific processes (essentially the scripting languages) // we include the arguments in the falco hash as well // if(m_comm.size() == 4) { uint32_t ncomm = *(uint32_t*)m_comm.c_str(); if(ncomm == STR_AS_NUM_JAVA || ncomm == STR_AS_NUM_RUBY || ncomm == STR_AS_NUM_PERL || ncomm == STR_AS_NUM_NODE) { m_program_hash_falco = m_program_hash; } } else if(m_comm.size() >= 6) { if(m_comm.substr(0, 6) == "python") { m_program_hash_falco = m_program_hash; } } } void sinsp_threadinfo::add_fd_from_scap(scap_fdinfo *fdi, OUT sinsp_fdinfo_t *res) { sinsp_fdinfo_t* newfdi = res; newfdi->reset(); bool do_add = true; newfdi->m_type = fdi->type; newfdi->m_openflags = 0; newfdi->m_type = fdi->type; newfdi->m_flags = sinsp_fdinfo_t::FLAGS_FROM_PROC; newfdi->m_ino = fdi->ino; switch(newfdi->m_type) { case SCAP_FD_IPV4_SOCK: newfdi->m_sockinfo.m_ipv4info.m_fields.m_sip = fdi->info.ipv4info.sip; newfdi->m_sockinfo.m_ipv4info.m_fields.m_dip = fdi->info.ipv4info.dip; newfdi->m_sockinfo.m_ipv4info.m_fields.m_sport = fdi->info.ipv4info.sport; newfdi->m_sockinfo.m_ipv4info.m_fields.m_dport = fdi->info.ipv4info.dport; newfdi->m_sockinfo.m_ipv4info.m_fields.m_l4proto = fdi->info.ipv4info.l4proto; if(fdi->info.ipv4info.l4proto == SCAP_L4_TCP) { newfdi->m_flags |= sinsp_fdinfo_t::FLAGS_SOCKET_CONNECTED; } if(m_inspector->m_network_interfaces) { m_inspector->m_network_interfaces->update_fd(newfdi); } newfdi->m_name = ipv4tuple_to_string(&newfdi->m_sockinfo.m_ipv4info, m_inspector->m_hostname_and_port_resolution_enabled); break; case SCAP_FD_IPV4_SERVSOCK: newfdi->m_sockinfo.m_ipv4serverinfo.m_ip = fdi->info.ipv4serverinfo.ip; newfdi->m_sockinfo.m_ipv4serverinfo.m_port = fdi->info.ipv4serverinfo.port; newfdi->m_sockinfo.m_ipv4serverinfo.m_l4proto = fdi->info.ipv4serverinfo.l4proto; newfdi->m_name = ipv4serveraddr_to_string(&newfdi->m_sockinfo.m_ipv4serverinfo, m_inspector->m_hostname_and_port_resolution_enabled); // // We keep note of all the host bound server ports. // We'll need them later when patching connections direction. // m_inspector->m_thread_manager->m_server_ports.insert(newfdi->m_sockinfo.m_ipv4serverinfo.m_port); break; case SCAP_FD_IPV6_SOCK: if(sinsp_utils::is_ipv4_mapped_ipv6((uint8_t*)&fdi->info.ipv6info.sip) && sinsp_utils::is_ipv4_mapped_ipv6((uint8_t*)&fdi->info.ipv6info.dip)) { // // This is an IPv4-mapped IPv6 addresses (http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses). // Convert it into the IPv4 representation. // newfdi->m_type = SCAP_FD_IPV4_SOCK; newfdi->m_sockinfo.m_ipv4info.m_fields.m_sip = fdi->info.ipv6info.sip[3]; newfdi->m_sockinfo.m_ipv4info.m_fields.m_dip = fdi->info.ipv6info.dip[3]; newfdi->m_sockinfo.m_ipv4info.m_fields.m_sport = fdi->info.ipv6info.sport; newfdi->m_sockinfo.m_ipv4info.m_fields.m_dport = fdi->info.ipv6info.dport; newfdi->m_sockinfo.m_ipv4info.m_fields.m_l4proto = fdi->info.ipv6info.l4proto; if(fdi->info.ipv6info.l4proto == SCAP_L4_TCP) { newfdi->m_flags |= sinsp_fdinfo_t::FLAGS_SOCKET_CONNECTED; } if(m_inspector->m_network_interfaces) { m_inspector->m_network_interfaces->update_fd(newfdi); } newfdi->m_name = ipv4tuple_to_string(&newfdi->m_sockinfo.m_ipv4info, m_inspector->m_hostname_and_port_resolution_enabled); } else { copy_ipv6_address(newfdi->m_sockinfo.m_ipv6info.m_fields.m_sip.m_b, fdi->info.ipv6info.sip); copy_ipv6_address(newfdi->m_sockinfo.m_ipv6info.m_fields.m_dip.m_b, fdi->info.ipv6info.dip); newfdi->m_sockinfo.m_ipv6info.m_fields.m_sport = fdi->info.ipv6info.sport; newfdi->m_sockinfo.m_ipv6info.m_fields.m_dport = fdi->info.ipv6info.dport; newfdi->m_sockinfo.m_ipv6info.m_fields.m_l4proto = fdi->info.ipv6info.l4proto; if(fdi->info.ipv6info.l4proto == SCAP_L4_TCP) { newfdi->m_flags |= sinsp_fdinfo_t::FLAGS_SOCKET_CONNECTED; } newfdi->m_name = ipv6tuple_to_string(&newfdi->m_sockinfo.m_ipv6info, m_inspector->m_hostname_and_port_resolution_enabled); } break; case SCAP_FD_IPV6_SERVSOCK: copy_ipv6_address(newfdi->m_sockinfo.m_ipv6serverinfo.m_ip.m_b, fdi->info.ipv6serverinfo.ip); newfdi->m_sockinfo.m_ipv6serverinfo.m_port = fdi->info.ipv6serverinfo.port; newfdi->m_sockinfo.m_ipv6serverinfo.m_l4proto = fdi->info.ipv6serverinfo.l4proto; newfdi->m_name = ipv6serveraddr_to_string(&newfdi->m_sockinfo.m_ipv6serverinfo, m_inspector->m_hostname_and_port_resolution_enabled); // // We keep note of all the host bound server ports. // We'll need them later when patching connections direction. // m_inspector->m_thread_manager->m_server_ports.insert(newfdi->m_sockinfo.m_ipv6serverinfo.m_port); break; case SCAP_FD_UNIX_SOCK: newfdi->m_sockinfo.m_unixinfo.m_fields.m_source = fdi->info.unix_socket_info.source; newfdi->m_sockinfo.m_unixinfo.m_fields.m_dest = fdi->info.unix_socket_info.destination; newfdi->m_name = fdi->info.unix_socket_info.fname; if(newfdi->m_name.empty()) { newfdi->set_role_client(); } else { newfdi->set_role_server(); } break; case SCAP_FD_FILE_V2: newfdi->m_openflags = fdi->info.regularinfo.open_flags; newfdi->m_name = fdi->info.regularinfo.fname; newfdi->m_dev = fdi->info.regularinfo.dev; if(newfdi->m_name == USER_EVT_DEVICE_NAME) { newfdi->m_flags |= sinsp_fdinfo_t::FLAGS_IS_TRACER_FILE; } else { newfdi->m_flags |= sinsp_fdinfo_t::FLAGS_IS_NOT_TRACER_FD; } break; case SCAP_FD_FIFO: case SCAP_FD_FILE: case SCAP_FD_DIRECTORY: case SCAP_FD_UNSUPPORTED: case SCAP_FD_SIGNALFD: case SCAP_FD_EVENTPOLL: case SCAP_FD_EVENT: case SCAP_FD_INOTIFY: case SCAP_FD_TIMERFD: case SCAP_FD_NETLINK: newfdi->m_name = fdi->info.fname; if(newfdi->m_name == USER_EVT_DEVICE_NAME) { newfdi->m_flags |= sinsp_fdinfo_t::FLAGS_IS_TRACER_FILE; } else { newfdi->m_flags |= sinsp_fdinfo_t::FLAGS_IS_NOT_TRACER_FD; } break; default: ASSERT(false); do_add = false; break; } // // Call the protocol decoder callbacks associated to notify them about this FD // ASSERT(m_inspector != NULL); vector::iterator it; for(it = m_inspector->m_parser->m_open_callbacks.begin(); it != m_inspector->m_parser->m_open_callbacks.end(); ++it) { (*it)->on_fd_from_proc(newfdi); } // // Add the FD to the table // if(do_add) { m_fdtable.add(fdi->fd, newfdi); } } void sinsp_threadinfo::init(scap_threadinfo* pi) { scap_fdinfo *fdi; scap_fdinfo *tfdi; init(); m_tid = pi->tid; m_pid = pi->pid; m_ptid = pi->ptid; m_sid = pi->sid; m_vpgid = pi->vpgid; m_comm = pi->comm; m_exe = pi->exe; m_exepath = pi->exepath; set_args(pi->args, pi->args_len); if(is_main_thread()) { set_env(pi->env, pi->env_len); set_cwd(pi->cwd, (uint32_t)strlen(pi->cwd)); } m_flags |= pi->flags; m_flags |= PPM_CL_ACTIVE; // Assume that all the threads coming from /proc are real, active threads m_fdtable.clear(); m_fdlimit = pi->fdlimit; m_uid = pi->uid; m_gid = pi->gid; m_vmsize_kb = pi->vmsize_kb; m_vmrss_kb = pi->vmrss_kb; m_vmswap_kb = pi->vmswap_kb; m_pfmajor = pi->pfmajor; m_pfminor = pi->pfminor; m_nchilds = 0; m_vtid = pi->vtid; m_vpid = pi->vpid; m_clone_ts = pi->clone_ts; m_tty = pi->tty; m_loginuid = pi->loginuid; m_category = CAT_NONE; set_cgroups(pi->cgroups, pi->cgroups_len); m_root = pi->root; ASSERT(m_inspector); m_inspector->m_container_manager.resolve_container(this, !m_inspector->is_capture()); // // Prepare for filtering // sinsp_fdinfo_t tfdinfo; sinsp_evt tevt; scap_evt tscapevt; // // Initialize the fake events for filtering // tscapevt.ts = 0; tscapevt.type = PPME_SYSCALL_READ_X; tscapevt.len = 0; tscapevt.nparams = 0; tevt.m_inspector = m_inspector; tevt.m_info = &(g_infotables.m_event_info[PPME_SYSCALL_READ_X]); tevt.m_pevt = NULL; tevt.m_cpuid = 0; tevt.m_evtnum = 0; tevt.m_pevt = &tscapevt; bool match = false; HASH_ITER(hh, pi->fdlist, fdi, tfdi) { add_fd_from_scap(fdi, &tfdinfo); if(m_inspector->m_filter != NULL && m_inspector->m_filter_proc_table_when_saving) { tevt.m_tinfo = this; tevt.m_fdinfo = &tfdinfo; tscapevt.tid = m_tid; int64_t tlefd = tevt.m_tinfo->m_lastevent_fd; tevt.m_tinfo->m_lastevent_fd = fdi->fd; if(m_inspector->m_filter->run(&tevt)) { match = true; } else { // // This tells scap not to include this FD in the write file // fdi->type = SCAP_FD_UNINITIALIZED; } tevt.m_tinfo->m_lastevent_fd = tlefd; } } m_lastevent_data = NULL; if(m_inspector->m_filter != NULL && m_inspector->m_filter_proc_table_when_saving) { if(!match) { pi->filtered_out = 1; } } } string sinsp_threadinfo::get_comm() { return m_comm; } string sinsp_threadinfo::get_exe() { return m_exe; } string sinsp_threadinfo::get_exepath() { return m_exepath; } void sinsp_threadinfo::set_args(const char* args, size_t len) { m_args.clear(); size_t offset = 0; while(offset < len) { m_args.push_back(args + offset); offset += m_args.back().length() + 1; } } void sinsp_threadinfo::set_env(const char* env, size_t len) { if (len == SCAP_MAX_ENV_SIZE && m_inspector->large_envs_enabled()) { // the environment is possibly truncated, try to read from /proc // this may fail for short-lived processes if (set_env_from_proc()) { g_logger.format(sinsp_logger::SEV_DEBUG, "Large environment for process %lu [%s], loaded from /proc", m_pid, m_comm.c_str()); return; } else { g_logger.format(sinsp_logger::SEV_INFO, "Failed to load environment for process %lu [%s] from /proc, using first %d bytes", m_pid, m_comm.c_str(), SCAP_MAX_ENV_SIZE); } } m_env.clear(); size_t offset = 0; while(offset < len) { const char* left = env + offset; // environment string may actually be shorter than indicated by len // if the rest is empty, we bail out early if(!strlen(left)) { size_t sz = len - offset; void* zero = calloc(sz, sizeof(char)); if(!memcmp(left, zero, sz)) { free(zero); return; } free(zero); } m_env.push_back(left); offset += m_env.back().length() + 1; } } bool sinsp_threadinfo::set_env_from_proc() { string environ_path = string(scap_get_host_root()) + "/proc/" + to_string(m_pid) + "/environ"; ifstream environment(environ_path); if (!environment) { // failed to read the environment from /proc, work with what we have return false; } m_env.clear(); while (environment) { string env; getline(environment, env, '\0'); if (!env.empty()) { m_env.emplace_back(env); } } return true; } const vector& sinsp_threadinfo::get_env() { if(is_main_thread()) { return m_env; } else { auto mtinfo = get_main_thread(); if(mtinfo != nullptr) { return mtinfo->get_env(); } else { // it should never happen but provide a safe fallback just in case // except during sinsp::scap_open() (see sinsp::get_thread()). ASSERT(false); return m_env; } } } // Return value string for the exact environment variable name given string sinsp_threadinfo::get_env(const string& name) { size_t nlen = name.length(); for(const auto& env_var : get_env()) { if((env_var.length() > (nlen + 1)) && (env_var[nlen] == '=') && !env_var.compare(0, nlen, name)) { // Stripping spaces, not sure if we really should or need to size_t first = env_var.find_first_not_of(' ', nlen + 1); if (first == string::npos) return ""; size_t last = env_var.find_last_not_of(' '); return env_var.substr(first, last - first + 1); } } return ""; } void sinsp_threadinfo::set_cgroups(const char* cgroups, size_t len) { m_cgroups.clear(); size_t offset = 0; while(offset < len) { const char* str = cgroups + offset; const char* sep = strrchr(str, '='); if(sep == NULL) { ASSERT(false); return; } string subsys(str, sep - str); string cgroup(sep + 1); size_t subsys_length = subsys.length(); size_t pos = subsys.find("_cgroup"); if(pos != string::npos) { subsys.erase(pos, sizeof("_cgroup") - 1); } if(subsys == "perf") { subsys = "perf_event"; } else if(subsys == "mem") { subsys = "memory"; } else if(subsys == "io") { // blkio has been renamed just `io` // in kernel space: // https://github.com/torvalds/linux/commit/c165b3e3c7bb68c2ed55a5ac2623f030d01d9567 subsys = "blkio"; } m_cgroups.push_back(std::make_pair(subsys, cgroup)); offset += subsys_length + 1 + cgroup.length() + 1; } } sinsp_threadinfo* sinsp_threadinfo::get_parent_thread() { return m_inspector->get_thread(m_ptid, false, true); } sinsp_fdinfo_t* sinsp_threadinfo::add_fd(int64_t fd, sinsp_fdinfo_t *fdinfo) { sinsp_fdinfo_t* res = get_fd_table()->add(fd, fdinfo); // // Update the last event fd. It's needed by the filtering engine // m_lastevent_fd = fd; return res; } void sinsp_threadinfo::remove_fd(int64_t fd) { get_fd_table()->erase(fd); } bool sinsp_threadinfo::is_bound_to_port(uint16_t number) { unordered_map::iterator it; sinsp_fdtable* fdt = get_fd_table(); for(it = fdt->m_table.begin(); it != fdt->m_table.end(); ++it) { if(it->second.m_type == SCAP_FD_IPV4_SOCK) { if(it->second.m_sockinfo.m_ipv4info.m_fields.m_dport == number) { return true; } } else if(it->second.m_type == SCAP_FD_IPV4_SERVSOCK) { if(it->second.m_sockinfo.m_ipv4serverinfo.m_port == number) { return true; } } } return false; } bool sinsp_threadinfo::uses_client_port(uint16_t number) { unordered_map::iterator it; sinsp_fdtable* fdt = get_fd_table(); for(it = fdt->m_table.begin(); it != fdt->m_table.end(); ++it) { if(it->second.m_type == SCAP_FD_IPV4_SOCK) { if(it->second.m_sockinfo.m_ipv4info.m_fields.m_sport == number) { return true; } } } return false; } bool sinsp_threadinfo::is_lastevent_data_valid() { return (m_lastevent_cpuid != (uint16_t) - 1); } sinsp_threadinfo* sinsp_threadinfo::get_cwd_root() { if(!(m_flags & PPM_CL_CLONE_FS)) { return this; } else { return get_main_thread(); } } string sinsp_threadinfo::get_cwd() { // Ideally we should use get_cwd_root() // but scap does not read CLONE_FS from /proc // Also glibc and muslc use always // CLONE_THREAD|CLONE_FS so let's use // get_main_thread() for now sinsp_threadinfo* tinfo = get_main_thread(); if(tinfo) { return tinfo->m_cwd; } else { ASSERT(false); return "./"; } } void sinsp_threadinfo::set_cwd(const char* cwd, uint32_t cwdlen) { char tpath[SCAP_MAX_PATH_SIZE]; sinsp_threadinfo* tinfo = get_main_thread(); if(tinfo) { sinsp_utils::concatenate_paths(tpath, SCAP_MAX_PATH_SIZE, (char*)tinfo->m_cwd.c_str(), (uint32_t)tinfo->m_cwd.size(), cwd, cwdlen); tinfo->m_cwd = tpath; uint32_t size = tinfo->m_cwd.size(); if(size == 0 || (tinfo->m_cwd[size - 1] != '/')) { tinfo->m_cwd += '/'; } } else { ASSERT(false); } } void sinsp_threadinfo::allocate_private_state() { uint32_t j = 0; if(m_inspector != NULL) { m_private_state.clear(); vector* sizes = &m_inspector->m_thread_privatestate_manager.m_memory_sizes; for(j = 0; j < sizes->size(); j++) { void* newbuf = malloc(sizes->at(j)); memset(newbuf, 0, sizes->at(j)); m_private_state.push_back(newbuf); } } } void* sinsp_threadinfo::get_private_state(uint32_t id) { if(id >= m_private_state.size()) { ASSERT(false); throw sinsp_exception("invalid thread state ID" + to_string((long long) id)); } return m_private_state[id]; } uint64_t sinsp_threadinfo::get_fd_usage_pct() { int64_t fdlimit = get_fd_limit(); if(fdlimit > 0) { uint64_t fd_opencount = get_fd_opencount(); ASSERT(fd_opencount <= (uint64_t) fdlimit); if(fd_opencount <= (uint64_t) fdlimit) { return (fd_opencount * 100) / fdlimit; } else { return 100; } } else { return 0; } } double sinsp_threadinfo::get_fd_usage_pct_d() { int64_t fdlimit = get_fd_limit(); if(fdlimit > 0) { uint64_t fd_opencount = get_fd_opencount(); ASSERT(fd_opencount <= (uint64_t) fdlimit); if(fd_opencount <= (uint64_t) fdlimit) { return ((double)fd_opencount * 100) / fdlimit; } else { return 100; } } else { return 0; } } uint64_t sinsp_threadinfo::get_fd_opencount() { return get_main_thread()->m_fdtable.size(); } uint64_t sinsp_threadinfo::get_fd_limit() { return get_main_thread()->m_fdlimit; } void sinsp_threadinfo::traverse_parent_state(visitor_func_t &visitor) { // Use two pointers starting at this, traversing the parent // state, at different rates. If they ever equal each other // before slow is NULL there's a loop. sinsp_threadinfo *slow=this->get_parent_thread(), *fast=slow; // Move fast to its parent fast = (fast ? fast->get_parent_thread() : fast); // The slow pointer must be valid and not have a tid of -1. while(slow && slow->m_tid != -1) { if(!visitor(slow)) { break; } // Advance slow one step and advance fast two steps slow = slow->get_parent_thread(); // advance fast 2 steps, checking to see if we meet // slow after each step. for (uint32_t i = 0; i < 2; i++) { fast = (fast ? fast->get_parent_thread() : fast); // If not at the end but fast == slow or if // slow points to itself, there's a loop in // the thread state. if(slow && (slow == fast || slow->m_tid == slow->m_ptid)) { // Note we only log a loop once for a given main thread, to avoid flooding logs. if(!m_parent_loop_detected) { g_logger.log(string("Loop in parent thread state detected for pid ") + std::to_string(m_pid) + ". stopped at tid= " + std::to_string(slow->m_tid) + " ptid=" + std::to_string(slow->m_ptid), sinsp_logger::SEV_WARNING); m_parent_loop_detected = true; } return; } } } } void sinsp_threadinfo::populate_cmdline(string &cmdline, sinsp_threadinfo *tinfo) { cmdline = tinfo->get_comm(); uint32_t j; uint32_t nargs = (uint32_t)tinfo->m_args.size(); for(j = 0; j < nargs; j++) { cmdline += " " + tinfo->m_args[j]; } } bool sinsp_threadinfo::is_health_probe() { return (m_category == sinsp_threadinfo::CAT_HEALTHCHECK || m_category == sinsp_threadinfo::CAT_LIVENESS_PROBE || m_category == sinsp_threadinfo::CAT_READINESS_PROBE); } shared_ptr sinsp_threadinfo::lookup_thread() { return m_inspector->get_thread_ref(m_pid, true, true); } // // Note: this is duplicated here because visual studio has trouble inlining // the method. // #if defined(_WIN64) || defined(WIN64) || defined(_WIN32) || defined(WIN32) sinsp_threadinfo* sinsp_threadinfo::get_main_thread() { auto main_thread = m_main_thread.lock(); if (!main_thread) { // // Is this a child thread? // if (m_pid == m_tid) { // // No, this is either a single thread process or the root thread of a // multithread process. // Note: we don't set m_main_thread because there are cases in which this is // invoked for a threadinfo that is in the stack. Caching the this pointer // would cause future mess. // return this; } else { // // Yes, this is a child thread. Find the process root thread. // auto ptinfo = lookup_thread(); if (!ptinfo) { return NULL; } m_main_thread = ptinfo; return &*ptinfo; } } return &*main_thread; } #endif size_t sinsp_threadinfo::args_len() const { return strvec_len(m_args); } size_t sinsp_threadinfo::env_len() const { return strvec_len(m_env); } size_t sinsp_threadinfo::cgroups_len() const { size_t totlen = 0; for(auto &cgroup : m_cgroups) { totlen += cgroup.first.size() + 1 + cgroup.second.size(); totlen++; // Trailing NULL } return totlen; } void sinsp_threadinfo::args_to_iovec(struct iovec **iov, int *iovcnt, std::string &rem) const { return strvec_to_iovec(m_args, iov, iovcnt, rem); } void sinsp_threadinfo::env_to_iovec(struct iovec **iov, int *iovcnt, std::string &rem) const { return strvec_to_iovec(m_env, iov, iovcnt, rem); } // Set the provided iovec to the string in str, if it will fit. If it // won't, copy the portion that will fit to rem and set the iovec to // rem. Updates alen with the new total length and possibly sets rem // to any truncated string. void sinsp_threadinfo::add_to_iovec(const string &str, const bool include_trailing_null, struct iovec &iov, uint32_t &alen, std::string &rem) const { uint32_t len = str.size() + (include_trailing_null ? 1 : 0); const char *buf = str.c_str(); if(len > alen) { // The entire string won't fit. Use rem to hold a // truncated copy rem = str.substr(0, alen-1); buf = rem.c_str(); len = alen; } iov.iov_base = (void *) buf; iov.iov_len = len; alen -= len; } // iov will be allocated and must be freed. rem is used to hold a // possibly truncated final argument. void sinsp_threadinfo::cgroups_to_iovec(struct iovec **iov, int *iovcnt, std::string &rem) const { uint32_t alen = SCAP_MAX_ARGS_SIZE; static const string eq = "="; // We allocate an iovec big enough to hold all the cgroups and // intermediate '=' signs. Based on alen, we might not use all // of the iovec. *iov = (struct iovec *) malloc((3 * m_cgroups.size()) * sizeof(struct iovec)); *iovcnt = 0; for(auto it = m_cgroups.begin(); it != m_cgroups.end() && alen > 0; ++it) { add_to_iovec(it->first, false, (*iov)[(*iovcnt)++], alen, rem); if(alen > 0) { add_to_iovec(eq, false, (*iov)[(*iovcnt)++], alen, rem); } if(alen > 0) { add_to_iovec(it->second, true, (*iov)[(*iovcnt)++], alen, rem); } } } size_t sinsp_threadinfo::strvec_len(const vector &strs) const { size_t totlen = 0; for(auto &str : strs) { totlen += str.size(); totlen++; // Trailing NULL } return totlen; } // iov will be allocated and must be freed. rem is used to hold a // possibly truncated final argument. void sinsp_threadinfo::strvec_to_iovec(const vector &strs, struct iovec **iov, int *iovcnt, std::string &rem) const { uint32_t alen = SCAP_MAX_ARGS_SIZE; // We allocate an iovec big enough to hold all the entries in // strs. Based on alen, we might not use all of the iovec. *iov = (struct iovec *) malloc(strs.size() * sizeof(struct iovec)); *iovcnt = 0; for(auto it = strs.begin(); it != strs.end() && alen > 0; ++it) { add_to_iovec(*it, true, (*iov)[(*iovcnt)++], alen, rem); } } void sinsp_threadinfo::fd_to_scap(scap_fdinfo *dst, sinsp_fdinfo_t* src) { dst->type = src->m_type; dst->ino = src->m_ino; switch(dst->type) { case SCAP_FD_IPV4_SOCK: dst->info.ipv4info.sip = src->m_sockinfo.m_ipv4info.m_fields.m_sip; dst->info.ipv4info.dip = src->m_sockinfo.m_ipv4info.m_fields.m_dip; dst->info.ipv4info.sport = src->m_sockinfo.m_ipv4info.m_fields.m_sport; dst->info.ipv4info.dport = src->m_sockinfo.m_ipv4info.m_fields.m_dport; dst->info.ipv4info.l4proto = src->m_sockinfo.m_ipv4info.m_fields.m_l4proto; break; case SCAP_FD_IPV4_SERVSOCK: dst->info.ipv4serverinfo.ip = src->m_sockinfo.m_ipv4serverinfo.m_ip; dst->info.ipv4serverinfo.port = src->m_sockinfo.m_ipv4serverinfo.m_port; dst->info.ipv4serverinfo.l4proto = src->m_sockinfo.m_ipv4serverinfo.m_l4proto; break; case SCAP_FD_IPV6_SOCK: copy_ipv6_address(dst->info.ipv6info.sip, src->m_sockinfo.m_ipv6info.m_fields.m_sip.m_b); copy_ipv6_address(dst->info.ipv6info.dip, src->m_sockinfo.m_ipv6info.m_fields.m_dip.m_b); dst->info.ipv6info.sport = src->m_sockinfo.m_ipv6info.m_fields.m_sport; dst->info.ipv6info.dport = src->m_sockinfo.m_ipv6info.m_fields.m_dport; dst->info.ipv6info.l4proto = src->m_sockinfo.m_ipv6info.m_fields.m_l4proto; break; case SCAP_FD_IPV6_SERVSOCK: copy_ipv6_address(dst->info.ipv6serverinfo.ip, src->m_sockinfo.m_ipv6serverinfo.m_ip.m_b); dst->info.ipv6serverinfo.port = src->m_sockinfo.m_ipv6serverinfo.m_port; dst->info.ipv6serverinfo.l4proto = src->m_sockinfo.m_ipv6serverinfo.m_l4proto; break; case SCAP_FD_UNIX_SOCK: dst->info.unix_socket_info.source = src->m_sockinfo.m_unixinfo.m_fields.m_source; dst->info.unix_socket_info.destination = src->m_sockinfo.m_unixinfo.m_fields.m_dest; strncpy(dst->info.unix_socket_info.fname, src->m_name.c_str(), SCAP_MAX_PATH_SIZE); break; case SCAP_FD_FILE_V2: dst->info.regularinfo.open_flags = src->m_openflags; strncpy(dst->info.regularinfo.fname, src->m_name.c_str(), SCAP_MAX_PATH_SIZE); dst->info.regularinfo.dev = src->m_dev; break; case SCAP_FD_FIFO: case SCAP_FD_FILE: case SCAP_FD_DIRECTORY: case SCAP_FD_UNSUPPORTED: case SCAP_FD_SIGNALFD: case SCAP_FD_EVENTPOLL: case SCAP_FD_EVENT: case SCAP_FD_INOTIFY: case SCAP_FD_TIMERFD: case SCAP_FD_NETLINK: strncpy(dst->info.fname, src->m_name.c_str(), SCAP_MAX_PATH_SIZE); break; default: ASSERT(false); break; } } /////////////////////////////////////////////////////////////////////////////// // sinsp_thread_manager implementation /////////////////////////////////////////////////////////////////////////////// sinsp_thread_manager::sinsp_thread_manager(sinsp* inspector) { m_inspector = inspector; m_listener = NULL; clear(); } void sinsp_thread_manager::clear() { m_threadtable.clear(); m_last_tid = 0; m_last_tinfo.reset(); m_last_flush_time_ns = 0; m_n_drops = 0; #ifdef GATHER_INTERNAL_STATS m_failed_lookups = &m_inspector->m_stats.get_metrics_registry().register_counter(internal_metrics::metric_name("thread_failed_lookups","Failed thread lookups")); m_cached_lookups = &m_inspector->m_stats.get_metrics_registry().register_counter(internal_metrics::metric_name("thread_cached_lookups","Cached thread lookups")); m_non_cached_lookups = &m_inspector->m_stats.get_metrics_registry().register_counter(internal_metrics::metric_name("thread_non_cached_lookups","Non cached thread lookups")); m_added_threads = &m_inspector->m_stats.get_metrics_registry().register_counter(internal_metrics::metric_name("thread_added","Number of added threads")); m_removed_threads = &m_inspector->m_stats.get_metrics_registry().register_counter(internal_metrics::metric_name("thread_removed","Removed threads")); #endif } void sinsp_thread_manager::set_listener(sinsp_threadtable_listener* listener) { m_listener = listener; } void sinsp_thread_manager::increment_mainthread_childcount(sinsp_threadinfo* threadinfo) { if(threadinfo->m_flags & PPM_CL_CLONE_THREAD) { // // Increment the refcount of the main thread so it won't // be deleted (if it calls pthread_exit()) until we are done // ASSERT(threadinfo->m_pid != threadinfo->m_tid); sinsp_threadinfo* main_thread = m_inspector->get_thread(threadinfo->m_pid, true, true); if(main_thread) { ++main_thread->m_nchilds; } else { ASSERT(false); } } } bool sinsp_thread_manager::add_thread(sinsp_threadinfo *threadinfo, bool from_scap_proctable) { #ifdef GATHER_INTERNAL_STATS m_added_threads->increment(); #endif m_last_tinfo.reset(); if (m_threadtable.size() >= m_inspector->m_max_thread_table_size #if defined(HAS_CAPTURE) && threadinfo->m_pid != m_inspector->m_sysdig_pid #endif ) { // rate limit messages to avoid spamming the logs if (m_n_drops % m_inspector->m_max_thread_table_size == 0) { g_logger.format(sinsp_logger::SEV_INFO, "Thread table full, dropping tid %lu (pid %lu, comm \"%s\")", threadinfo->m_tid, threadinfo->m_pid, threadinfo->m_comm.c_str()); } m_n_drops++; return false; } if(!from_scap_proctable) { increment_mainthread_childcount(threadinfo); } threadinfo->compute_program_hash(); threadinfo->allocate_private_state(); m_threadtable.put(threadinfo); if(m_listener) { m_listener->on_thread_created(threadinfo); } return true; } void sinsp_thread_manager::remove_thread(int64_t tid, bool force) { uint64_t nchilds; sinsp_threadinfo* tinfo = m_threadtable.get(tid); if(tinfo == nullptr) { // // Looks like there's no thread to remove. // Either the thread creation event was dropped or our logic doesn't support the // call that created this thread. The assertion will detect it, while in release mode we just // keep going. // #ifdef GATHER_INTERNAL_STATS m_failed_lookups->increment(); #endif return; } else if((nchilds = tinfo->m_nchilds) == 0 || force) { // // Decrement the refcount of the main thread/program because // this reference is gone // if(tinfo->m_flags & PPM_CL_CLONE_THREAD) { ASSERT(tinfo->m_pid != tinfo->m_tid); sinsp_threadinfo* main_thread = m_inspector->get_thread(tinfo->m_pid, false, true); if(main_thread) { if(main_thread->m_nchilds > 0) { --main_thread->m_nchilds; } else { ASSERT(false); } } else { ASSERT(false); } } // // If this is the main thread of a process, erase all the FDs that the process owns // if(tinfo->m_pid == tinfo->m_tid) { unordered_map* fdtable = &(tinfo->get_fd_table()->m_table); unordered_map::iterator fdit; erase_fd_params eparams; eparams.m_remove_from_table = false; eparams.m_inspector = m_inspector; eparams.m_tinfo = tinfo; eparams.m_ts = m_inspector->m_lastevent_ts; for(fdit = fdtable->begin(); fdit != fdtable->end(); ++fdit) { eparams.m_fd = fdit->first; // // The canceled fd should always be deleted immediately, so if it appears // here it means we have a problem. // ASSERT(eparams.m_fd != CANCELED_FD_NUMBER); eparams.m_fdinfo = &(fdit->second); m_inspector->m_parser->erase_fd(&eparams); } } // // Reset the cache // m_last_tid = 0; m_last_tinfo.reset(); #ifdef GATHER_INTERNAL_STATS m_removed_threads->increment(); #endif m_threadtable.erase(tid); // // If the thread has a nonzero refcount, it means that we are forcing the removal // of a main process or program that some child refer to. // We need to recalculate the child relationships, or the table will become // corrupted. // if(nchilds != 0) { recreate_child_dependencies(); } } } void sinsp_thread_manager::fix_sockets_coming_from_proc() { m_threadtable.loop([&] (sinsp_threadinfo& tinfo) { tinfo.fix_sockets_coming_from_proc(); return true; }); } void sinsp_thread_manager::clear_thread_pointers(sinsp_threadinfo& tinfo) { tinfo.m_main_thread.reset(); sinsp_fdtable* fdt = tinfo.get_fd_table(); if(fdt != NULL) { fdt->reset_cache(); } } /* void sinsp_thread_manager::clear_thread_pointers(threadinfo_map_iterator_t it) { it->second.m_main_program_thread = NULL; it->second.m_main_thread = NULL; it->second.m_progid = -1LL; it->second.m_fdtable.reset_cache(); } */ void sinsp_thread_manager::reset_child_dependencies() { m_last_tinfo.reset(); m_last_tid = 0; m_threadtable.loop([&] (sinsp_threadinfo& tinfo) { tinfo.m_nchilds = 0; clear_thread_pointers(tinfo); return true; }); } void sinsp_thread_manager::create_child_dependencies() { m_threadtable.loop([&] (sinsp_threadinfo& tinfo) { increment_mainthread_childcount(&tinfo); return true; }); } void sinsp_thread_manager::recreate_child_dependencies() { reset_child_dependencies(); create_child_dependencies(); } void sinsp_thread_manager::update_statistics() { #ifdef GATHER_INTERNAL_STATS m_inspector->m_stats.m_n_threads = get_thread_count(); m_inspector->m_stats.m_n_fds = 0; for(threadinfo_map_iterator_t it = m_threadtable.begin(); it != m_threadtable.end(); it++) { m_inspector->m_stats.m_n_fds += it->second.get_fd_table()->size(); } #endif } void sinsp_thread_manager::free_dump_fdinfos(vector* fdinfos_to_free) { for(uint32_t j = 0; j < fdinfos_to_free->size(); j++) { free(fdinfos_to_free->at(j)); } fdinfos_to_free->clear(); } // NOTE: This does *not* populate any array-based fields (comm, exe, // exepath, args, env, cwd, cgroups, root) void sinsp_thread_manager::thread_to_scap(sinsp_threadinfo& tinfo, scap_threadinfo* sctinfo) { // // Fill in the thread data // // NOTE: This is doing a shallow copy of the strings from // tinfo, and is valid only as long as tinfo is valid. sctinfo->tid = tinfo.m_tid; sctinfo->pid = tinfo.m_pid; sctinfo->ptid = tinfo.m_ptid; sctinfo->sid = tinfo.m_sid; sctinfo->vpgid = tinfo.m_vpgid; sctinfo->flags = tinfo.m_flags ; sctinfo->fdlimit = tinfo.m_fdlimit; sctinfo->uid = tinfo.m_uid; sctinfo->gid = tinfo.m_gid; sctinfo->vmsize_kb = tinfo.m_vmsize_kb; sctinfo->vmrss_kb = tinfo.m_vmrss_kb; sctinfo->vmswap_kb = tinfo.m_vmswap_kb; sctinfo->pfmajor = tinfo.m_pfmajor; sctinfo->pfminor = tinfo.m_pfminor; sctinfo->vtid = tinfo.m_vtid; sctinfo->vpid = tinfo.m_vpid; sctinfo->fdlist = NULL; sctinfo->loginuid = tinfo.m_loginuid; sctinfo->filtered_out = false; } void sinsp_thread_manager::dump_threads_to_file(scap_dumper_t* dumper) { // // First pass of the table to calculate the lengths // uint32_t totlen = 0; vector lengths; m_threadtable.loop([&] (sinsp_threadinfo& tinfo) { uint32_t il = (uint32_t) (sizeof(uint32_t) + // len sizeof(uint64_t) + // tid sizeof(uint64_t) + // pid sizeof(uint64_t) + // ptid sizeof(uint64_t) + // sid sizeof(uint64_t) + // pgid 2 + MIN(tinfo.m_comm.size(), SCAP_MAX_PATH_SIZE) + 2 + MIN(tinfo.m_exe.size(), SCAP_MAX_PATH_SIZE) + 2 + MIN(tinfo.m_exepath.size(), SCAP_MAX_PATH_SIZE) + 2 + MIN(tinfo.args_len(), SCAP_MAX_ARGS_SIZE) + // 1 is sizeof("/") 2 + MIN((tinfo.m_cwd == "")? 1 : tinfo.m_cwd.size(), SCAP_MAX_PATH_SIZE) + sizeof(uint64_t) + // fdlimit sizeof(uint32_t) + // flags sizeof(uint32_t) + // uid sizeof(uint32_t) + // gid sizeof(uint32_t) + // vmsize_kb sizeof(uint32_t) + // vmrss_kb sizeof(uint32_t) + // vmswap_kb sizeof(uint64_t) + // pfmajor sizeof(uint64_t) + // pfminor 2 + MIN(tinfo.env_len(), SCAP_MAX_ENV_SIZE) + sizeof(int64_t) + // vtid sizeof(int64_t) + // vpid 2 + MIN(tinfo.cgroups_len(), SCAP_MAX_CGROUPS_SIZE) + 2 + MIN(tinfo.m_root.size(), SCAP_MAX_PATH_SIZE)) + sizeof(uint32_t); // loginuid lengths.push_back(il); totlen += il; return true; }); // // Second pass of the table to dump the Threads // if(scap_write_proclist_header(m_inspector->m_h, dumper, totlen) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); } uint32_t idx = 0; m_threadtable.loop([&] (sinsp_threadinfo& tinfo) { scap_threadinfo *sctinfo; struct iovec *args_iov, *envs_iov, *cgroups_iov; int argscnt, envscnt, cgroupscnt; string argsrem, envsrem, cgroupsrem; if((sctinfo = scap_proc_alloc(m_inspector->m_h)) == NULL) { throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); } thread_to_scap(tinfo, sctinfo); tinfo.args_to_iovec(&args_iov, &argscnt, argsrem); tinfo.env_to_iovec(&envs_iov, &envscnt, envsrem); tinfo.cgroups_to_iovec(&cgroups_iov, &cgroupscnt, cgroupsrem); if(scap_write_proclist_entry_bufs(m_inspector->m_h, dumper, sctinfo, lengths[idx++], tinfo.m_comm.c_str(), tinfo.m_exe.c_str(), tinfo.m_exepath.c_str(), args_iov, argscnt, envs_iov, envscnt, (tinfo.m_cwd == "" ? "/" : tinfo.m_cwd.c_str()), cgroups_iov, cgroupscnt, tinfo.m_root.c_str()) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); } free(args_iov); free(envs_iov); free(cgroups_iov); scap_proc_free(m_inspector->m_h, sctinfo); return true; }); if(scap_write_proclist_trailer(m_inspector->m_h, dumper, totlen) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); } // // Third pass of the table to dump the FDs // m_threadtable.loop([&] (sinsp_threadinfo& tinfo) { scap_threadinfo *sctinfo; if((sctinfo = scap_proc_alloc(m_inspector->m_h)) == NULL) { throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); } // Note: as scap_fd_add/scap_write_proc_fds do not use // any of the array-based fields like comm, etc. a // shallow copy is safe thread_to_scap(tinfo, sctinfo); if(tinfo.is_main_thread()) { // // Add the FDs // unordered_map& fdtable = tinfo.get_fd_table()->m_table; for(auto it = fdtable.begin(); it != fdtable.end(); ++it) { // // Allocate the scap fd info // scap_fdinfo* scfdinfo = (scap_fdinfo*)malloc(sizeof(scap_fdinfo)); if(scfdinfo == NULL) { scap_proc_free(m_inspector->m_h, sctinfo); throw sinsp_exception("thread memory allocation error in sinsp_thread_manager::to_scap"); } // // Populate the fd info // scfdinfo->fd = it->first; tinfo.fd_to_scap(scfdinfo, &it->second); // // Add the new fd to the scap table. // if(scap_fd_add(m_inspector->m_h, sctinfo, it->first, scfdinfo) != SCAP_SUCCESS) { scap_proc_free(m_inspector->m_h, sctinfo); throw sinsp_exception("error calling scap_fd_add in sinsp_thread_manager::to_scap (" + string(scap_getlasterr(m_inspector->m_h)) + ")"); } } } // // Dump the thread to disk // if(scap_write_proc_fds(m_inspector->m_h, sctinfo, dumper) != SCAP_SUCCESS) { scap_proc_free(m_inspector->m_h, sctinfo); throw sinsp_exception("error calling scap_proc_add in sinsp_thread_manager::to_scap (" + string(scap_getlasterr(m_inspector->m_h)) + ")"); } scap_proc_free(m_inspector->m_h, sctinfo); return true; }); } sysdig-0.26.4/userspace/libsinsp/threadinfo.h000066400000000000000000000364551352731327100212370ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #ifndef VISIBILITY_PRIVATE #define VISIBILITY_PRIVATE private: #endif #ifdef _WIN32 struct iovec { void *iov_base; /* Starting address */ size_t iov_len; /* Number of bytes to transfer */ }; #else #include #endif #include #include #include #include "fdinfo.h" #include "internal_metrics.h" class sinsp_delays_info; class sinsp_threadtable_listener; class thread_analyzer_info; class sinsp_tracerparser; class blprogram; typedef struct erase_fd_params { bool m_remove_from_table; sinsp* m_inspector; int64_t m_fd; sinsp_threadinfo* m_tinfo; sinsp_fdinfo_t* m_fdinfo; uint64_t m_ts; }erase_fd_params; /** @defgroup state State management * @{ */ /*! \brief Thread/process information class. This class contains the full state for a thread, and a bunch of functions to manipulate threads and retrieve thread information. \note As a library user, you won't need to construct thread objects. Rather, you get them by calling \ref sinsp_evt::get_thread_info or \ref sinsp::get_thread. \note sinsp_threadinfo is also used to keep process state. For the sinsp library, a process is just a thread with TID=PID. */ class SINSP_PUBLIC sinsp_threadinfo { public: sinsp_threadinfo(); sinsp_threadinfo(sinsp *inspector); ~sinsp_threadinfo(); /*! \brief Return the name of the process containing this thread, e.g. "top". */ std::string get_comm(); /*! \brief Return the name of the process containing this thread from argv[0], e.g. "/bin/top". */ std::string get_exe(); /*! \brief Return the full executable path of the process containing this thread, e.g. "/bin/top". */ std::string get_exepath(); /*! \brief Return the working directory of the process containing this thread. */ std::string get_cwd(); /*! \brief Return the values of all environment variables for the process containing this thread. */ const std::vector& get_env(); /*! \brief Return the value of the specified environment variable for the process containing this thread. Returns empty string if variable is not found. */ std::string get_env(const std::string& name); /*! \brief Return true if this is a process' main thread. */ inline bool is_main_thread() const { return m_tid == m_pid; } /*! \brief Get the main thread of the process containing this thread. */ #ifndef _WIN32 inline sinsp_threadinfo* get_main_thread() { auto main_thread = m_main_thread.lock(); if(!main_thread) { // // Is this a child thread? // if(m_pid == m_tid) { // // No, this is either a single thread process or the root thread of a // multithread process. // Note: we don't set m_main_thread because there are cases in which this is // invoked for a threadinfo that is in the stack. Caching the this pointer // would cause future mess. // return this; } else { // // Yes, this is a child thread. Find the process root thread. // auto ptinfo = lookup_thread(); if (!ptinfo) { return NULL; } m_main_thread = ptinfo; return &*ptinfo; } } return &*main_thread; } #else sinsp_threadinfo* get_main_thread(); #endif /*! \brief Get the process that launched this thread's process. */ sinsp_threadinfo* get_parent_thread(); /*! \brief Retrieve information about one of this thread/process FDs. \param fd The file descriptor number, e.g. 0 for stdin. \return Pointer to the FD information, or NULL if the given FD doesn't exist */ inline sinsp_fdinfo_t* get_fd(int64_t fd) { if(fd < 0) { return NULL; } sinsp_fdtable* fdt = get_fd_table(); if(fdt) { sinsp_fdinfo_t *fdinfo = fdt->find(fd); if(fdinfo) { // Its current name is now its old // name. The name might change as a // result of parsing. fdinfo->m_oldname = fdinfo->m_name; return fdinfo; } } return NULL; } /*! \brief Return true if this thread is bound to the given server port. */ bool is_bound_to_port(uint16_t number); /*! \brief Return true if this thread has a client socket open on the given port. */ bool uses_client_port(uint16_t number); void* get_private_state(uint32_t id); /*! \brief Return the ratio between open FDs and maximum available FDs for this thread. */ uint64_t get_fd_usage_pct(); double get_fd_usage_pct_d(); /*! \brief Return the number of open FDs for this thread. */ uint64_t get_fd_opencount(); /*! \brief Return the maximum number of FDs this thread can open. */ uint64_t get_fd_limit(); // // Walk up the parent process hierarchy, calling the provided // function for each node. If the function returns false, the // traversal stops. // typedef std::function visitor_func_t; void traverse_parent_state(visitor_func_t &visitor); static void populate_cmdline(std::string &cmdline, sinsp_threadinfo *tinfo); // Return true if this thread is a part of a healthcheck, // readiness probe, or liveness probe. bool is_health_probe(); // // Core state // int64_t m_tid; ///< The id of this thread int64_t m_pid; ///< The id of the process containing this thread. In single thread threads, this is equal to tid. int64_t m_ptid; ///< The id of the process that started this thread. int64_t m_sid; ///< The session id of the process containing this thread. std::string m_comm; ///< Command name (e.g. "top") std::string m_exe; ///< argv[0] (e.g. "sshd: user@pts/4") std::string m_exepath; ///< full executable path std::vector m_args; ///< Command line arguments (e.g. "-d1") std::vector m_env; ///< Environment variables std::vector> m_cgroups; ///< subsystem-cgroup pairs std::string m_container_id; ///< heuristic-based container id uint32_t m_flags; ///< The thread flags. See the PPM_CL_* declarations in ppm_events_public.h. int64_t m_fdlimit; ///< The maximum number of FDs this thread can open uint32_t m_uid; ///< user id uint32_t m_gid; ///< group id uint64_t m_nchilds; ///< When this is 0 the process can be deleted uint32_t m_vmsize_kb; ///< total virtual memory (as kb). uint32_t m_vmrss_kb; ///< resident non-swapped memory (as kb). uint32_t m_vmswap_kb; ///< swapped memory (as kb). uint64_t m_pfmajor; ///< number of major page faults since start. uint64_t m_pfminor; ///< number of minor page faults since start. int64_t m_vtid; ///< The virtual id of this thread. int64_t m_vpid; ///< The virtual id of the process containing this thread. In single thread threads, this is equal to vtid. int64_t m_vpgid; // The virtual process group id, as seen from its pid namespace std::string m_root; size_t m_program_hash; size_t m_program_hash_falco; int32_t m_tty; int32_t m_loginuid; ///< loginuid (auid) // In some cases, a threadinfo has a category that identfies // why it was run. Descriptions: // CAT_NONE: no specific category // CAT_CONTAINER: a process run in a container and *not* any // of the following more specific categories. // CAT_HEALTHCHECK: part of a container healthcheck // CAT_LIVENESS_PROBE: part of a k8s liveness probe // CAT_READINESS_PROBE: part of a k8s readiness probe enum command_category { CAT_NONE = 0, CAT_CONTAINER, CAT_HEALTHCHECK, CAT_LIVENESS_PROBE, CAT_READINESS_PROBE }; command_category m_category; // // State for multi-event processing // int64_t m_lastevent_fd; ///< The FD os the last event used by this thread. uint64_t m_lastevent_ts; ///< timestamp of the last event for this thread. uint64_t m_prevevent_ts; ///< timestamp of the event before the last for this thread. uint64_t m_lastaccess_ts; ///< The last time this thread was looked up. Used when cleaning up the table. uint64_t m_clone_ts; ///< When the clone that started this process happened. // // Parser for the user events. Public so that filter fields can access it // sinsp_tracerparser* m_tracer_parser; thread_analyzer_info* m_ainfo; size_t args_len() const; size_t env_len() const; size_t cgroups_len() const; void args_to_iovec(struct iovec **iov, int *iovcnt, std::string &rem) const; void env_to_iovec(struct iovec **iov, int *iovcnt, std::string &rem) const; void cgroups_to_iovec(struct iovec **iov, int *iovcnt, std::string &rem) const; #ifdef HAS_FILTERING // // State for filtering // uint64_t m_last_latency_entertime; uint64_t m_latency; #endif // // Global state // sinsp *m_inspector; public: // types required for use in sets struct hasher { size_t operator()(sinsp_threadinfo* tinfo) const { return tinfo->get_main_thread()->m_program_hash; } }; struct comparer { size_t operator()(sinsp_threadinfo* lhs, sinsp_threadinfo* rhs) const { return lhs->get_main_thread()->m_program_hash == rhs->get_main_thread()->m_program_hash; } }; VISIBILITY_PRIVATE void init(); // return true if, based on the current inspector filter, this thread should be kept void init(scap_threadinfo* pi); void fix_sockets_coming_from_proc(); sinsp_fdinfo_t* add_fd(int64_t fd, sinsp_fdinfo_t *fdinfo); void add_fd_from_scap(scap_fdinfo *fdinfo, OUT sinsp_fdinfo_t *res); void remove_fd(int64_t fd); inline sinsp_fdtable* get_fd_table() { sinsp_threadinfo* root; if(!(m_flags & PPM_CL_CLONE_FILES)) { root = this; } else { root = get_main_thread(); if(NULL == root) { return NULL; } } return &(root->m_fdtable); } void set_cwd(const char *cwd, uint32_t cwdlen); sinsp_threadinfo* get_cwd_root(); void set_args(const char* args, size_t len); void set_env(const char* env, size_t len); bool set_env_from_proc(); void set_cgroups(const char* cgroups, size_t len); bool is_lastevent_data_valid(); inline void set_lastevent_data_validity(bool isvalid) { if(isvalid) { m_lastevent_cpuid = (uint16_t)1; } else { m_lastevent_cpuid = (uint16_t) - 1; } } void allocate_private_state(); void compute_program_hash(); std::shared_ptr lookup_thread(); size_t strvec_len(const std::vector &strs) const; void strvec_to_iovec(const std::vector &strs, struct iovec **iov, int *iovcnt, std::string &rem) const; void add_to_iovec(const std::string &str, const bool include_trailing_null, struct iovec &iov, uint32_t &alen, std::string &rem) const; void fd_to_scap(scap_fdinfo *dst, sinsp_fdinfo_t* src); // void push_fdop(sinsp_fdop* op); // the queue of recent fd operations // std::deque m_last_fdop; // // Parameters that can't be accessed directly because they could be in the // parent thread info // sinsp_fdtable m_fdtable; // The fd table of this thread std::string m_cwd; // current working directory std::weak_ptr m_main_thread; uint8_t* m_lastevent_data; // Used by some event parsers to store the last enter event std::vector m_private_state; uint16_t m_lastevent_type; uint16_t m_lastevent_cpuid; sinsp_evt::category m_lastevent_category; bool m_parent_loop_detected; blprogram* m_blprogram; friend class sinsp; friend class sinsp_parser; friend class sinsp_analyzer; friend class sinsp_analyzer_parsers; friend class sinsp_evt; friend class sinsp_thread_manager; friend class sinsp_transaction_table; friend class thread_analyzer_info; friend class sinsp_tracerparser; friend class lua_cbacks; friend class sinsp_baseliner; }; /*@}*/ class threadinfo_map_t { public: typedef std::function visitor_t; typedef std::shared_ptr ptr_t; inline void put(sinsp_threadinfo* tinfo) { m_threads[tinfo->m_tid] = ptr_t(tinfo); } inline sinsp_threadinfo* get(uint64_t tid) { auto it = m_threads.find(tid); if (it == m_threads.end()) { return nullptr; } return it->second.get(); } inline ptr_t get_ref(uint64_t tid) { auto it = m_threads.find(tid); if (it == m_threads.end()) { return nullptr; } return it->second; } inline void erase(uint64_t tid) { m_threads.erase(tid); } inline void clear() { m_threads.clear(); } bool loop(visitor_t callback) { for (auto& it : m_threads) { if (!callback(*it.second.get())) { return false; } } return true; } inline size_t size() const { return m_threads.size(); } protected: std::unordered_map m_threads; }; /////////////////////////////////////////////////////////////////////////////// // Little class that manages the allocation of private state in the thread info class /////////////////////////////////////////////////////////////////////////////// class sinsp_thread_privatestate_manager { public: // // The return value is the ID of the newly reserved memory area // uint32_t reserve(uint32_t size) { m_memory_sizes.push_back(size); return (uint32_t)m_memory_sizes.size() - 1; } uint32_t get_size() { return (uint32_t)m_memory_sizes.size(); } private: std::vector m_memory_sizes; friend class sinsp_threadinfo; }; /////////////////////////////////////////////////////////////////////////////// // This class manages the thread table /////////////////////////////////////////////////////////////////////////////// class SINSP_PUBLIC sinsp_thread_manager { public: sinsp_thread_manager(sinsp* inspector); void clear(); void set_listener(sinsp_threadtable_listener* listener); bool add_thread(sinsp_threadinfo *threadinfo, bool from_scap_proctable); void remove_thread(int64_t tid, bool force); // Returns true if the table is actually scanned // NOTE: this is implemented in sinsp.cpp so we can inline it from there inline bool remove_inactive_threads(); void fix_sockets_coming_from_proc(); void reset_child_dependencies(); void create_child_dependencies(); void recreate_child_dependencies(); void dump_threads_to_file(scap_dumper_t* dumper); uint32_t get_thread_count() { return (uint32_t)m_threadtable.size(); } void update_statistics(); threadinfo_map_t* get_threads() { return &m_threadtable; } std::set m_server_ports; private: void increment_mainthread_childcount(sinsp_threadinfo* threadinfo); inline void clear_thread_pointers(sinsp_threadinfo& threadinfo); void free_dump_fdinfos(std::vector* fdinfos_to_free); void thread_to_scap(sinsp_threadinfo& tinfo, scap_threadinfo* sctinfo); sinsp* m_inspector; threadinfo_map_t m_threadtable; int64_t m_last_tid; std::weak_ptr m_last_tinfo; uint64_t m_last_flush_time_ns; uint32_t m_n_drops; sinsp_threadtable_listener* m_listener; INTERNAL_COUNTER(m_failed_lookups); INTERNAL_COUNTER(m_cached_lookups); INTERNAL_COUNTER(m_non_cached_lookups); INTERNAL_COUNTER(m_added_threads); INTERNAL_COUNTER(m_removed_threads); friend class sinsp_parser; friend class sinsp_analyzer; friend class sinsp; friend class sinsp_threadinfo; friend class sinsp_baseliner; }; sysdig-0.26.4/userspace/libsinsp/token_bucket.cpp000066400000000000000000000031521352731327100221100ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "sinsp.h" #include "utils.h" #include "token_bucket.h" token_bucket::token_bucket() { init(1, 1); } token_bucket::~token_bucket() { } void token_bucket::init(double rate, double max_tokens, uint64_t now) { m_rate = rate; m_max_tokens = max_tokens; m_tokens = max_tokens; if(now == 0) { now = sinsp_utils::get_current_time_ns(); } m_last_seen = now; } bool token_bucket::claim() { uint64_t now = sinsp_utils::get_current_time_ns(); return claim(1, now); } bool token_bucket::claim(double tokens, uint64_t now) { double tokens_gained = m_rate * ((now - m_last_seen) / (1000000000.0)); m_last_seen = now; m_tokens += tokens_gained; // // Cap at max_tokens // if(m_tokens > m_max_tokens) { m_tokens = m_max_tokens; } // // If m_tokens is < tokens, can't claim. // if(m_tokens < tokens) { return false; } m_tokens -= tokens; return true; } double token_bucket::get_tokens() { return m_tokens; } uint64_t token_bucket::get_last_seen() { return m_last_seen; } sysdig-0.26.4/userspace/libsinsp/token_bucket.h000066400000000000000000000034711352731327100215610ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include // A simple token bucket that accumulates tokens at a fixed rate and allows // for limited bursting in the form of "banked" tokens. class token_bucket { public: token_bucket(); virtual ~token_bucket(); // // Initialize the token bucket and start accumulating tokens // void init(double rate, double max_tokens, uint64_t now = 0); // // Try to claim tokens tokens from the token bucket, using a // timestamp of now. Returns true if the tokens could be // claimed. Also updates internal metrics. // bool claim(double tokens, uint64_t now); // Simpler version of claim that claims a single token and // uses the current time for now bool claim(); // Return the current number of tokens available double get_tokens(); // Return the last time someone tried to claim a token. uint64_t get_last_seen(); private: // // The number of tokens generated per second. // double m_rate; // // The maximum number of tokens that can be banked for future // claim()s. // double m_max_tokens; // // The current number of tokens // double m_tokens; // // The last time claim() was called (or the object was created). // Nanoseconds since the epoch. // uint64_t m_last_seen; }; sysdig-0.26.4/userspace/libsinsp/tracer_emitter.cpp000066400000000000000000000111711352731327100224440ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "tracer_emitter.h" #include "sinsp.h" #include "sinsp_int.h" #include "analyzer_utils.h" #include #include #include thread_local int tls_fd = -1; // Helper class to allow multiple tracer_emitter instances to // share a single connection to /dev/null. Multiple threads can // write safely and without locking by storing the /dev/null // fd in thread local storage. Locking is only required when // the fd is created or destroyed, and that should only happen // at startup. class tracer_writer { public: tracer_writer() {} ~tracer_writer() { close_fd(); } void write(const std::string &trc); private: int get_fd(); void close_fd(); int m_fd = -1; run_on_interval m_open_interval = 60 * ONE_SECOND_IN_NS; std::mutex m_fd_lock; constexpr static const char *m_file = "/dev/null"; }; void tracer_writer::write(const std::string &trc) { if (tls_fd < 0) { tls_fd = get_fd(); if (tls_fd < 0) { // Something is wrong with /dev/null, // so all writes are going to drop return; } } ASSERT(tls_fd >= 0); // Writes to /dev/null should always succeed. // Still, error check because if m_fd changes // for some reason, tls_fd needs to get // cleared so we pick up the new m_fd next time. auto ret = ::write(tls_fd, trc.c_str(), trc.length()); if (ret < 0 && errno == EINTR) { // Try once more before giving up ret = ::write(tls_fd, trc.c_str(), trc.length()); } if (ret < 0 && errno != EINTR) { g_logger.format(sinsp_logger::SEV_ERROR, "Unable to write tracer (%s) to %s: %s", trc.c_str(), m_file, strerror(errno)); close_fd(); } // We know ret >= 0 so size_t cast is safe else if ((size_t)ret != trc.length()) { ASSERT(false); g_logger.format(sinsp_logger::SEV_ERROR, "Incomplete write of tracer (%s) to %s", trc.c_str(), m_file); close_fd(); } return; } int tracer_writer::get_fd() { std::lock_guard lock(m_fd_lock); if (m_fd >= 0) { return m_fd; } m_open_interval.run( [this]() { g_logger.format(sinsp_logger::SEV_DEBUG, "Opening %s for writing tracers", m_file); m_fd = ::open(m_file, O_WRONLY|O_NONBLOCK|O_CLOEXEC); if (m_fd < 0) { g_logger.format(sinsp_logger::SEV_ERROR, "Unable to open %s for writing tracers: %s", m_file, strerror(errno)); } }); return m_fd; } void tracer_writer::close_fd() { std::lock_guard lock(m_fd_lock); if (m_fd > -1) { g_logger.format(sinsp_logger::SEV_DEBUG, "Closing %s (fd %d) for writing tracers", m_file, m_fd); ::close(m_fd); m_fd = -1; } } tracer_emitter::tracer_emitter(std::string tag, uint64_t timeout_ns) : m_tag(std::move(tag)) , m_start_ns(sinsp_utils::get_current_time_ns()) , m_timeout_ns(timeout_ns) { start(); } bool tracer_emitter::m_enabled = false; // XXX find/write a constexpr-compatible string class // for compile time concatenation tracer_emitter::tracer_emitter(std::string tag, const tracer_emitter &parent, uint64_t timeout_ns) : tracer_emitter::tracer_emitter( parent.tag() + '.' + std::move(tag), std::min(timeout_ns, parent.m_timeout_ns)) { } tracer_emitter::~tracer_emitter() { if (!m_exit_written) { write_tracer(false); elapsed_time(); // just for the side effect of logging if needed } } void tracer_emitter::start() { write_tracer(true); } uint64_t tracer_emitter::stop() { ASSERT(!m_exit_written); if (!m_exit_written) { write_tracer(false); } return elapsed_time(); } void tracer_emitter::write_tracer(const bool enter) { if (!m_enabled) { return; } static tracer_writer trc_writer; // XXX can we constexpr this part too? std::string trc_str(enter ? ">" : "<"); // 't' == use thread id trc_str.append(":t:"); trc_str.append(m_tag); trc_str.append("::"); trc_writer.write(trc_str); if (!enter) { m_exit_written = true; } } uint64_t tracer_emitter::elapsed_time() const { auto elapsed = sinsp_utils::get_current_time_ns() - m_start_ns; if (elapsed > m_timeout_ns) { g_logger.format(sinsp_logger::SEV_INFO, "Tracer %s elapsed time %llu ns", m_tag.c_str(), elapsed); } return elapsed; } sysdig-0.26.4/userspace/libsinsp/tracer_emitter.h000066400000000000000000000031071352731327100221110ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include // This class allows the caller to output sysdig tracers // to /dev/null. class tracer_emitter { public: static const uint64_t no_timeout = ~0ULL; tracer_emitter(std::string tag, uint64_t timeout_ns=no_timeout); tracer_emitter(std::string tag, const tracer_emitter &parent, uint64_t timeout_ns=no_timeout); ~tracer_emitter(); tracer_emitter() = delete; tracer_emitter(const tracer_emitter&) = delete; tracer_emitter& operator=(const tracer_emitter&) = delete; // Stop is only needed if you want the exit // event before the instance gets destructed, // i.e. goes out of scope uint64_t stop(); static void set_enabled(bool enabled) { m_enabled = enabled; } private: void start(); void write_tracer(const bool enter); const std::string& tag() const { return m_tag; } uint64_t elapsed_time() const; const std::string m_tag; const uint64_t m_start_ns = 0; const uint64_t m_timeout_ns = 0; bool m_exit_written = false; static bool m_enabled; }; sysdig-0.26.4/userspace/libsinsp/tracers.cpp000066400000000000000000000561221352731327100211030ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "sinsp.h" #include "sinsp_int.h" #include "tracers.h" sinsp_tracerparser::sinsp_tracerparser(sinsp *inspector) { m_inspector = inspector; m_storage_size = 0; m_storage = NULL; m_res = sinsp_tracerparser::RES_OK; m_fragment_size = 0; m_enter_pae = NULL; } sinsp_tracerparser::~sinsp_tracerparser() { if(m_storage) { free(m_storage); } } void sinsp_tracerparser::set_storage_size(uint32_t newsize) { m_storage = (char*)realloc(m_storage, newsize); if(m_storage == NULL) { throw sinsp_exception("memory allocation error in sinsp_tracerparser::process_event_data."); } m_storage_size = newsize; } sinsp_tracerparser::parse_result sinsp_tracerparser::process_event_data(char *data, uint32_t datalen, uint64_t ts) { ASSERT(data != NULL); m_storlen = m_fragment_size + datalen; // // Make sure we have enough space in the buffer and copy the data into it // if(m_storage_size < m_storlen + 1) { set_storage_size(m_storlen + 1); } memcpy(m_storage + m_fragment_size, data, datalen); m_storage[m_storlen] = 0; if(m_fragment_size != 0) { m_fullfragment_storage_str = m_storage; } // // Do the parsing // if(m_storlen > 0) { // // Reset the content // m_res = sinsp_tracerparser::RES_OK; m_tags.clear(); m_argnames.clear(); m_argvals.clear(); m_taglens.clear(); m_argnamelens.clear(); m_argvallens.clear(); m_tot_taglens = 0; m_tot_argnamelens = 0; m_tot_argvallens = 0; if(m_storage[0] == '>' || m_storage[0] == '<') { parse_simple(m_storage); } else { parse_json(m_storage); } } else { m_res = sinsp_tracerparser::RES_FAILED; } if(m_res == sinsp_tracerparser::RES_FAILED) { // // Invalid syntax // m_fragment_size = 0; m_fullfragment_storage_str.clear(); return m_res; } else if(m_res == sinsp_tracerparser::RES_TRUNCATED) { // // Valid syntax, but the message is incomplete. Buffer it and wait for // more fragments. // if(m_fragment_size > MAX_USER_EVT_BUFFER) { // // Maximum buffering size reached, drop the event // m_fragment_size = 0; return m_res; } if(m_fullfragment_storage_str.length() == 0) { memcpy(m_storage, data, datalen); m_storage[datalen] = 0; m_fragment_size += datalen; } else { uint32_t tlen = (uint32_t)m_fullfragment_storage_str.length(); memcpy(m_storage, m_fullfragment_storage_str.c_str(), tlen); m_fragment_size = tlen; } return m_res; } m_fragment_size = 0; m_fullfragment_storage_str.clear(); // // Parser tests stop here // if(m_inspector == NULL) { return sinsp_tracerparser::RES_OK; } // // Event decoding done. We do state tracking only if explicitly requested // by one or more filters. // if(m_inspector->m_track_tracers_state == false) { return sinsp_tracerparser::RES_OK; } // // If this is an enter event, allocate a sinsp_partial_tracer object and // push it to the list // if(m_type_str[0] == '>') { sinsp_partial_tracer* pae = m_inspector->m_partial_tracers_pool->pop(); if(pae == NULL) { // // The list is completely used. This likely means that there have been drops and // the entries will be stuck there forever. Better clean the list, miss the 128 // events it contains, and start fresh. // list* partial_tracers_list = &m_inspector->m_partial_tracers_list; list::iterator it; for(it = partial_tracers_list->begin(); it != partial_tracers_list->end(); ++it) { m_inspector->m_partial_tracers_pool->push(*it); } partial_tracers_list->clear(); return sinsp_tracerparser::RES_OK; } init_partial_tracer(pae); pae->m_time = ts; m_inspector->m_partial_tracers_list.push_front(pae); m_enter_pae = pae; } else { list* partial_tracers_list = &m_inspector->m_partial_tracers_list; list::iterator it; init_partial_tracer(&m_exit_pae); for(it = partial_tracers_list->begin(); it != partial_tracers_list->end(); ++it) { if(m_exit_pae.compare(*it) == true) { m_exit_pae.m_time = ts; // // This is a bit tricky and deserves some explanation: // despite removing the pae and returning it to the available pool, // we link to it so that the filters will use it. We do that as an // optimization (it avoids making a copy or implementing logic for // delayed list removal), and we base it on the assumption that, // since the processing is strictly sequential and single thread, // nobody will modify the pae until the event is fully processed. // m_enter_pae = *it; m_inspector->m_partial_tracers_pool->push(*it); partial_tracers_list->erase(it); return sinsp_tracerparser::RES_OK; } } m_enter_pae = NULL; } return sinsp_tracerparser::RES_OK; } sinsp_partial_tracer* sinsp_tracerparser::find_parent_enter_pae() { list* partial_tracers_list = &m_inspector->m_partial_tracers_list; list::iterator it; char* tse = m_enter_pae->m_tags_storage + m_tot_taglens; if(*tse == 0 && tse > m_enter_pae->m_tags_storage) { --tse; } uint32_t len = 0; while(tse != m_enter_pae->m_tags_storage) { if(*tse == 0) { len = tse - m_enter_pae->m_tags_storage + 1; // 1 is for the traling zero break; } --tse; } for(it = partial_tracers_list->begin(); it != partial_tracers_list->end(); ++it) { if(m_enter_pae->compare(*it, len) == true) { return *it; } } return NULL; } inline void sinsp_tracerparser::parse_json(char* evtstr) { char* p = m_storage; uint32_t delta; char* tstr; // // Skip the initial bracket // m_res = skip_spaces(p, &delta); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; if(*(p++) != '[') { m_res = sinsp_tracerparser::RES_FAILED; return; } // // type // m_res = parsestr(p, &m_type_str, &delta); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; // // ID // m_res = skip_spaces_and_commas(p, &delta, 1); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; if(*p == '"') { switch(*(++p)) { case 't': m_id = m_tinfo->m_tid; delta = 2; break; case 'p': m_id = m_tinfo->m_pid; if(*(p + 1) == 'p') { m_id = m_tinfo->m_ptid; p++; } delta = 2; break; case ':': m_id = 0; delta = 1; break; case 'g': m_id = 0; delta = 2; break; default: m_res = sinsp_tracerparser::RES_FAILED; break; } } else { m_res = parsenumber(p, &m_id, &delta); if(m_res > sinsp_tracerparser::RES_COMMA) { return; } } p += delta; if(m_res == sinsp_tracerparser::RES_COMMA) { m_res = skip_spaces(p, &delta); } else { m_res = skip_spaces_and_commas(p, &delta, 1); } if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; // // First tag // m_res = skip_spaces_and_char(p, &delta, '['); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; m_res = parsestr_not_enforce(p, &tstr, &delta); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; if(tstr != NULL) { m_tags.push_back(tstr); m_taglens.push_back(delta - 2); m_tot_taglens += delta - 2; // // Remaining tags // while(true) { m_res = skip_spaces_and_commas(p, &delta, 0); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; if(*p == ']') { break; } m_res = parsestr(p, &tstr, &delta); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; m_tags.push_back(tstr); m_taglens.push_back(delta - 2); m_tot_taglens += delta - 2; } } // // First argument // m_res = skip_spaces_and_commas_and_all_brakets(p, &delta); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; m_res = parsestr_not_enforce(p, &tstr, &delta); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; if(tstr != NULL) { m_argnames.push_back(tstr); m_argnamelens.push_back(delta - 2); m_tot_argnamelens += delta - 2; m_res = skip_spaces_and_char(p, &delta, ':'); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; m_res = parsestr(p, &tstr, &delta); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; m_argvals.push_back(tstr); m_argvallens.push_back(delta - 2); m_tot_argvallens += delta - 2; // // Remaining arguments // while(true) { m_res = skip_spaces_and_commas_and_cr_brakets(p, &delta); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; if(*p == ']') { p++; break; } m_res = parsestr(p, &tstr, &delta); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; m_argnames.push_back(tstr); m_argnamelens.push_back(delta - 2); m_tot_argnamelens += delta - 2; m_res = skip_spaces_and_char(p, &delta, ':'); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; m_res = parsestr(p, &tstr, &delta); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; m_argvals.push_back(tstr); m_argvallens.push_back(delta - 2); m_tot_argvallens += delta - 2; } } // // Terminating ] // m_res = skip_spaces(p, &delta); if(m_res != sinsp_tracerparser::RES_OK) { return; } p += delta; if(*p != ']') { if(*p == 0) { m_res = sinsp_tracerparser::RES_TRUNCATED; } else { m_res = sinsp_tracerparser::RES_FAILED; } return; } m_res = sinsp_tracerparser::RES_OK; return; } inline void sinsp_tracerparser::delete_char(char* p) { while(*p != 0) { *p = *(p + 1); p++; } } inline void sinsp_tracerparser::parse_simple(char* evtstr) { char* p = evtstr; uint32_t delta; // // Extract the type // m_type_str = p++; // // Skip to the scope/id // if(*p != ':') { if(*p == 0) { m_res = sinsp_tracerparser::RES_TRUNCATED; } else { m_res = sinsp_tracerparser::RES_FAILED; } return; } *p = 0; p++; // // Extract the scope // if(*p == '0') { m_res = sinsp_tracerparser::RES_TRUNCATED; return; } switch(*p) { case 't': m_id = m_tinfo->m_tid; delta = 2; break; case 'p': m_id = m_tinfo->m_pid; if(*(p + 1) == 'p') { m_id = m_tinfo->m_ptid; p++; } delta = 2; break; case ':': m_id = 0; delta = 1; break; case 'g': m_id = 0; delta = 2; break; default: m_res = parsenumber_colend(p, &m_id, &delta); if(m_res > sinsp_tracerparser::RES_COMMA) { return; } break; } p += delta; // // Extract the tags // if(*p == '0') { m_res = sinsp_tracerparser::RES_TRUNCATED; return; } if(*p != ':') { bool dont_interpret_next_char = false; while(true) { char* start = p; m_tags.push_back(p); while(*p != 0) { if(dont_interpret_next_char) { dont_interpret_next_char = false; ++p; continue; } if(*p == '\\') { ASSERT(dont_interpret_next_char == false); dont_interpret_next_char = true; delete_char(p); continue; } if(*p == '.' || *p == ':') { break; } if(*p == '>' || *p == '<' || *p == '=' || *p == '\n') { m_res = sinsp_tracerparser::RES_FAILED; return; } ++p; } m_taglens.push_back((uint32_t)(p - start)); m_tot_taglens += (uint32_t)(p - start); if(*p == ':') { *p = 0; break; } else if(*p == 0) { m_res = sinsp_tracerparser::RES_TRUNCATED; return; } else { *p = 0; ++p; } } } ++p; // // Extract the arguments // if(*p == 0) { m_res = sinsp_tracerparser::RES_TRUNCATED; return; } if(*p != ':') { bool dont_interpret_next_char = false; while(true) { char* start = p; // // Arg name // m_argnames.push_back(p); while(*p != 0) { if(dont_interpret_next_char) { dont_interpret_next_char = false; ++p; continue; } if(*p == '\\') { ASSERT(dont_interpret_next_char == false); dont_interpret_next_char = true; delete_char(p); continue; } if(*p == '=') { break; } if(*p == '>' || *p == '<' || *p == '\n') { m_res = sinsp_tracerparser::RES_FAILED; return; } ++p; } m_argnamelens.push_back((uint32_t)(p - start)); m_tot_argnamelens += (uint32_t)(p - start); if(*p == 0) { if(*(p - 1) == ':') { // // This means there was an argument without value, // which we don't support // m_res = sinsp_tracerparser::RES_FAILED; } else { m_res = sinsp_tracerparser::RES_TRUNCATED; } break; } else { *p = 0; ++p; } // // Arg vals // start = p; m_argvals.push_back(p); dont_interpret_next_char = false; while(*p != 0) { if(dont_interpret_next_char) { dont_interpret_next_char = false; ++p; continue; } if(*p == '\\') { ASSERT(dont_interpret_next_char == false); dont_interpret_next_char = true; delete_char(p); continue; } if(*p == ',' || *p == ':' || *p == '=') { break; } ++p; } m_argvallens.push_back((uint32_t)(p - start)); m_tot_argvallens += (uint32_t)(p - start); if(*p == ':') { *p = 0; m_res = sinsp_tracerparser::RES_OK; break; } else if(*p == 0) { m_res = sinsp_tracerparser::RES_TRUNCATED; break; } else { *p = 0; ++p; } } } // // All done // return; } inline sinsp_tracerparser::parse_result sinsp_tracerparser::skip_spaces(char* p, uint32_t* delta) { char* start = p; while(*p == ' ') { if(*p == 0) { return sinsp_tracerparser::RES_TRUNCATED; } p++; } *delta = (uint32_t)(p - start); return sinsp_tracerparser::RES_OK; } inline sinsp_tracerparser::parse_result sinsp_tracerparser::skip_spaces_and_commas(char* p, uint32_t* delta, uint32_t n_expected_commas) { char* start = p; uint32_t nc = 0; while(true) { if(*p == ' ') { p++; continue; } else if(*p == ',') { nc++; } else if(*p == 0) { return sinsp_tracerparser::RES_TRUNCATED; } else { break; } p++; } if(nc < n_expected_commas) { return sinsp_tracerparser::RES_FAILED; } *delta = (uint32_t)(p - start); return sinsp_tracerparser::RES_OK; } inline sinsp_tracerparser::parse_result sinsp_tracerparser::skip_spaces_and_char(char* p, uint32_t* delta, char char_to_skip) { char* start = p; uint32_t nc = 0; while(*p == ' ' || *p == char_to_skip || *p == 0) { if(*p == 0) { return sinsp_tracerparser::RES_TRUNCATED; } else if(*p == char_to_skip) { nc++; } p++; } if(nc != 1) { return sinsp_tracerparser::RES_FAILED; } *delta = (uint32_t)(p - start); return sinsp_tracerparser::RES_OK; } inline sinsp_tracerparser::parse_result sinsp_tracerparser::skip_spaces_and_commas_and_sq_brakets(char* p, uint32_t* delta) { char* start = p; uint32_t nc = 0; uint32_t nosb = 0; while(*p == ' ' || *p == ',' || *p == '[' || *p == ']' || *p == 0) { if(*p == 0) { return sinsp_tracerparser::RES_TRUNCATED; } else if(*p == ',') { nc++; } else if(*p == '[') { nosb++; } else if(*p == ']') { if(nosb != 0) { break; } } p++; } if(nc != 1 || nosb != 1) { return sinsp_tracerparser::RES_FAILED; } *delta = (uint32_t)(p - start); return sinsp_tracerparser::RES_OK; } inline sinsp_tracerparser::parse_result sinsp_tracerparser::skip_spaces_and_commas_and_cr_brakets(char* p, uint32_t* delta) { char* start = p; uint32_t nc = 0; uint32_t nocb = 0; uint32_t nccb = 0; while(*p == ' ' || *p == ',' || *p == '{' || *p == '}' || *p == 0) { if(*p == 0) { return sinsp_tracerparser::RES_TRUNCATED; } else if(*p == ',') { nc++; } else if(*p == '{') { nocb++; } else if(*p == '}') { nccb++; } p++; } if(!((nc == 1 && nocb == 1) || (nc == 1 && nccb == 1) || (nccb == 1 && *p == ']'))) { return sinsp_tracerparser::RES_FAILED; } *delta = (uint32_t)(p - start); return sinsp_tracerparser::RES_OK; } inline sinsp_tracerparser::parse_result sinsp_tracerparser::skip_spaces_and_commas_and_all_brakets(char* p, uint32_t* delta) { char* start = p; uint32_t nc = 0; uint32_t nosb = 0; uint32_t nocb = 0; while(*p == ' ' || *p == ',' || *p == '[' || *p == ']' || *p == '{' || *p == '}' || (*p == 0)) { if(*p == 0) { return sinsp_tracerparser::RES_TRUNCATED; } else if(*p == ',') { nc++; } else if(*p == '[') { nosb++; } else if(*p == ']') { if(nosb != 0) { break; } } else if(*p == '{') { nocb++; } p++; } if(nc != 1 || nosb != 1) { return sinsp_tracerparser::RES_FAILED; } else if(nocb != 1) { if(*p != ']') { return sinsp_tracerparser::RES_FAILED; } } *delta = (uint32_t)(p - start); return sinsp_tracerparser::RES_OK; } inline sinsp_tracerparser::parse_result sinsp_tracerparser::parsestr(char* p, char** res, uint32_t* delta) { char* initial = p; *res = NULL; // // Make sure that we start with a \" // if(*p != '"') { *delta = (uint32_t)(p - initial + 1); if(*p == 0) { return sinsp_tracerparser::RES_TRUNCATED; } else { return sinsp_tracerparser::RES_FAILED; } } *res = p + 1; p++; // // Navigate to the end of the string // while(!(*p == '\"' && *(p - 1) != '\\')) { if(*p == 0) { *delta = (uint32_t)(p - initial + 1); return sinsp_tracerparser::RES_TRUNCATED; } p++; } *p = 0; *delta = (uint32_t)(p - initial + 1); return sinsp_tracerparser::RES_OK; } inline sinsp_tracerparser::parse_result sinsp_tracerparser::parsestr_not_enforce(char* p, char** res, uint32_t* delta) { sinsp_tracerparser::parse_result psres = parsestr(p, res, delta); if(psres == sinsp_tracerparser::RES_FAILED) { if(*(p + *delta) == ']') { *res = NULL; return sinsp_tracerparser::RES_OK; } } else if(psres == sinsp_tracerparser::RES_TRUNCATED) { return psres; } return sinsp_tracerparser::RES_OK; } inline sinsp_tracerparser::parse_result sinsp_tracerparser::parsenumber(char* p, int64_t* res, uint32_t* delta) { char* start = p; sinsp_tracerparser::parse_result retval = sinsp_tracerparser::RES_OK; int64_t val = 0; bool negative = false; if(*p == '-') { negative = true; p++; } while(*p >= '0' && *p <= '9') { val = val * 10 + (*p - '0'); p++; } if(*p == ',') { retval = sinsp_tracerparser::RES_COMMA; } else if(*p != 0 && *p != ' ') { return sinsp_tracerparser::RES_FAILED; } else if(*p == 0) { return sinsp_tracerparser::RES_TRUNCATED; } *p = 0; if(negative) { *res = -val; } else { *res = val; } *delta = (uint32_t)(p - start + 1); return retval; } inline sinsp_tracerparser::parse_result sinsp_tracerparser::parsenumber_colend(char* p, int64_t* res, uint32_t* delta) { char* start = p; int64_t val = 0; bool negative = false; if(*p == '-') { negative = true; p++; } while(*p >= '0' && *p <= '9') { val = val * 10 + (*p - '0'); p++; } if(*p != ':') { if(*p == 0) { return sinsp_tracerparser::RES_TRUNCATED; } else { return sinsp_tracerparser::RES_FAILED; } } else { *delta = (uint32_t)(p - start + 1); if(negative) { *res = -val; } else { *res = val; } return sinsp_tracerparser::RES_OK; } } inline void sinsp_tracerparser::init_partial_tracer(sinsp_partial_tracer* pae) { vector::iterator it; vector::iterator sit; ASSERT(m_tinfo != NULL); pae->m_tid = m_tinfo->m_tid; // // Store the ID // pae->m_id = m_id; ASSERT(m_tags.size() == m_taglens.size()); ASSERT(m_argnames.size() == m_argnamelens.size()); ASSERT(m_argvals.size() == m_argvallens.size()); // // Pack the tags // pae->m_tags.clear(); pae->m_taglens.clear(); pae->m_ntags = (uint32_t)m_tags.size(); uint32_t encoded_tags_len = m_tot_taglens + pae->m_ntags + 1; if(pae->m_tags_storage_size < encoded_tags_len) { pae->m_tags_storage = (char*)realloc(pae->m_tags_storage, encoded_tags_len); pae->m_tags_storage_size = encoded_tags_len; } char* p = pae->m_tags_storage; for(it = m_tags.begin(), sit = m_taglens.begin(); it != m_tags.end(); ++it, ++sit) { memcpy(p, *it, (*sit) + 1); pae->m_tags.push_back(p); pae->m_taglens.push_back(*sit); p += (*sit) + 1; } *p++ = 0; pae->m_tags_len = (uint32_t)(p - pae->m_tags_storage); // // Pack the argnames // pae->m_argnames.clear(); pae->m_argnamelens.clear(); pae->m_nargs = (uint32_t)m_argnames.size(); uint32_t encoded_argnames_len = m_tot_argnamelens + pae->m_nargs + 1; if(pae->m_argnames_storage_size < encoded_argnames_len) { pae->m_argnames_storage = (char*)realloc(pae->m_argnames_storage, encoded_argnames_len); pae->m_argnames_storage_size = encoded_argnames_len; } p = pae->m_argnames_storage; for(it = m_argnames.begin(), sit = m_argnamelens.begin(); it != m_argnames.end(); ++it, ++sit) { memcpy(p, *it, (*sit) + 1); pae->m_argnames.push_back(p); pae->m_argnamelens.push_back(*sit); p += (*sit) + 1; } *p++ = 0; pae->m_argnames_len = (uint32_t)(p - pae->m_argnames_storage); // // Pack the argvals // pae->m_argvals.clear(); pae->m_argvallens.clear(); uint32_t encoded_argvals_len = m_tot_argvallens + pae->m_nargs + 1; if(pae->m_argvals_storage_size < encoded_argvals_len) { pae->m_argvals_storage = (char*)realloc(pae->m_argvals_storage, encoded_argvals_len); pae->m_argvals_storage_size = encoded_argvals_len; } p = pae->m_argvals_storage; for(it = m_argvals.begin(), sit = m_argvallens.begin(); it != m_argvals.end(); ++it, ++sit) { memcpy(p, *it, (*sit) + 1); pae->m_argvals.push_back(p); pae->m_argvallens.push_back(*sit); p += (*sit) + 1; } *p++ = 0; pae->m_argvals_len = (uint32_t)(p - pae->m_argvals_storage); } void sinsp_tracerparser::test() { // char doc[] = "[\">\\\"\", 12435, [\"mysql\", \"query\", \"init\"], [{\"argname1\":\"argval1\"}, {\"argname2\":\"argval2\"}, {\"argname3\":\"argval3\"}]]"; // char doc1[] = "[\"\", 12345, [\"mysql\", \"query\", \"init\"], [{\"argname1\":\"argval1\"}, {\"argname2\":\"argval2\"}, {\"argname3\":\"argval3\"}]]"; // char doc1[] = ">:1111:u\\:\\=a.u\\:\\>.aaa.33.aa\\::a=b\\:\\=,c=d\\:\\=a:"; sinsp_threadinfo tinfo; m_tinfo = &tinfo; tinfo.m_ptid = 11; tinfo.m_pid = 22; tinfo.m_tid = 33; printf("1\n"); float cpu_time = ((float)clock ()) / CLOCKS_PER_SEC; for(uint64_t j = 0; j < 30000000; j++) { process_event_data(doc1, sizeof(doc1) - 1, 10); if(m_res != sinsp_tracerparser::RES_OK) { printf("ERROR\n"); } process_event_data(doc1, sizeof(doc1) - 1, 20); if(m_res != sinsp_tracerparser::RES_OK) { printf("ERROR\n"); } } cpu_time = ((float)clock()/ CLOCKS_PER_SEC) - cpu_time; printf ("time: %5.2f\n", cpu_time); } sysdig-0.26.4/userspace/libsinsp/tracers.h000066400000000000000000000113371352731327100205470ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #define UESTORAGE_INITIAL_BUFSIZE 256 /////////////////////////////////////////////////////////////////////////////// // A partial tracer /////////////////////////////////////////////////////////////////////////////// class sinsp_partial_tracer { public: sinsp_partial_tracer() { m_tags_storage = (char*)malloc(UESTORAGE_INITIAL_BUFSIZE); m_argnames_storage = (char*)malloc(UESTORAGE_INITIAL_BUFSIZE); m_argvals_storage = (char*)malloc(UESTORAGE_INITIAL_BUFSIZE); m_tags_storage_size = UESTORAGE_INITIAL_BUFSIZE; m_argnames_storage_size = UESTORAGE_INITIAL_BUFSIZE; m_argvals_storage_size = UESTORAGE_INITIAL_BUFSIZE; } ~sinsp_partial_tracer() { if(m_tags_storage) { free(m_tags_storage); } if(m_argnames_storage) { free(m_argnames_storage); } if(m_argvals_storage) { free(m_argvals_storage); } } inline bool compare(sinsp_partial_tracer* other) { if(m_id != other->m_id) { return false; } if(m_tags_len != other->m_tags_len) { return false; } if(memcmp(m_tags_storage, other->m_tags_storage, m_tags_len) == 0) { return true; } return false; } inline bool compare(sinsp_partial_tracer* other, uint32_t len) { if(m_id != other->m_id) { return false; } if(len != other->m_tags_len - 1) { return false; } if(memcmp(m_tags_storage, other->m_tags_storage, len) == 0) { return true; } return false; } char* m_tags_storage; char* m_argnames_storage; char* m_argvals_storage; uint32_t m_tags_len; uint32_t m_argnames_len; uint32_t m_argvals_len; uint32_t m_tags_storage_size; uint32_t m_argnames_storage_size; uint32_t m_argvals_storage_size; uint64_t m_id; vector m_tags; vector m_argnames; vector m_argvals; vector m_taglens; vector m_argnamelens; vector m_argvallens; uint32_t m_ntags; uint32_t m_nargs; uint64_t m_time; uint64_t m_tid; }; /////////////////////////////////////////////////////////////////////////////// // tracer parser /////////////////////////////////////////////////////////////////////////////// class sinsp_tracerparser { public: enum parse_result { RES_OK = 0, RES_COMMA = 1, RES_FAILED = 2, RES_TRUNCATED = 3, }; sinsp_tracerparser(sinsp *inspector); ~sinsp_tracerparser(); uint32_t get_storage_size() { return m_storage_size; } void set_storage_size(uint32_t newsize); parse_result process_event_data(char *data, uint32_t datalen, uint64_t ts); inline void parse_json(char* evtstr); inline void parse_simple(char* evtstr); sinsp_partial_tracer* find_parent_enter_pae(); void test(); char* m_type_str; int64_t m_id; vector m_tags; vector m_argnames; vector m_argvals; vector m_taglens; vector m_argnamelens; vector m_argvallens; pair*, vector*> m_args; uint32_t m_tot_taglens; uint32_t m_tot_argnamelens; uint32_t m_tot_argvallens; sinsp_partial_tracer* m_enter_pae; sinsp_partial_tracer m_exit_pae; sinsp_threadinfo* m_tinfo; VISIBILITY_PRIVATE inline parse_result skip_spaces(char* p, uint32_t* delta); inline parse_result skip_spaces_and_commas(char* p, uint32_t* delta, uint32_t n_expected_commas); inline parse_result skip_spaces_and_char(char* p, uint32_t* delta, char char_to_skip); inline parse_result skip_spaces_and_commas_and_sq_brakets(char* p, uint32_t* delta); inline parse_result skip_spaces_and_commas_and_cr_brakets(char* p, uint32_t* delta); inline parse_result skip_spaces_and_commas_and_all_brakets(char* p, uint32_t* delta); inline parse_result parsestr(char* p, char** res, uint32_t* delta); inline parse_result parsestr_not_enforce(char* p, char** res, uint32_t* delta); inline parse_result parsenumber(char* p, int64_t* res, uint32_t* delta); inline parse_result parsenumber_colend(char* p, int64_t* res, uint32_t* delta); inline void init_partial_tracer(sinsp_partial_tracer* pae); inline void delete_char(char* p); string m_fullfragment_storage_str; sinsp *m_inspector; char* m_storage; uint32_t m_storage_size; uint32_t m_fragment_size; sinsp_tracerparser::parse_result m_res; uint32_t m_storlen; friend class sinsp_parser; }; sysdig-0.26.4/userspace/libsinsp/tuples.cpp000066400000000000000000000026411352731327100207510ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include ipv6addr ipv6addr::empty_address = {0x00000000, 0x00000000, 0x00000000, 0x00000000}; bool ipv6addr::operator==(const ipv6addr &other) const { return (m_b[0] == other.m_b[0] && m_b[1] == other.m_b[1] && m_b[2] == other.m_b[2] && m_b[3] == other.m_b[3]); } bool ipv6addr::operator!=(const ipv6addr &other) const { return !operator==(other); } bool ipv6addr::operator<(const ipv6addr &other) const { for(int i = 0; i < 4; i++) { if(m_b[i] < other.m_b[i]) return true; else if(other.m_b[i] < m_b[i]) return false; } return false; } bool ipv6addr::in_subnet(const ipv6addr &other) const { // They're in the same subnet if the first 64 bits match // (Assumes convention of first 48 bits for network, next 16 // bits for subnet). return (m_b[0] == other.m_b[0] && m_b[1] == other.m_b[1]); } sysdig-0.26.4/userspace/libsinsp/tuples.h000066400000000000000000000052011352731327100204110ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include /** @defgroup state State management * @{ */ /*! \brief An IPv4 tuple. */ typedef union _ipv4tuple { struct { uint32_t m_sip; ///< Source (i.e. client) address. uint32_t m_dip; ///< Destination (i.e. server) address. uint16_t m_sport; ///< Source (i.e. client) port. uint16_t m_dport; ///< Destination (i.e. server) port. uint8_t m_l4proto; ///< Layer 4 protocol (e.g. TCP, UDP...). }m_fields; uint8_t m_all[13]; ///< The fields as a raw array ob bytes. Used for hasing. }ipv4tuple; /*! \brief An IPv4 network. */ typedef struct ipv4net { uint32_t m_ip; ///< IP addr uint32_t m_netmask; ///< Subnet mask }ipv4net; typedef struct _ipv6addr { uint32_t m_b[4]; bool operator==(const _ipv6addr &other) const; bool operator!=(const _ipv6addr &other) const; bool operator<(const _ipv6addr &other) const; bool in_subnet(const _ipv6addr &other) const; static struct _ipv6addr empty_address; }ipv6addr; /*! \brief An IPv6 tuple. */ typedef union _ipv6tuple { struct { ipv6addr m_sip; ///< source (i.e. client) address. ipv6addr m_dip; ///< destination (i.e. server) address. uint16_t m_sport; ///< source (i.e. client) port. uint16_t m_dport; ///< destination (i.e. server) port. uint8_t m_l4proto; ///< Layer 4 protocol (e.g. TCP, UDP...) } m_fields; uint8_t m_all[37]; ///< The fields as a raw array ob bytes. Used for hasing. } ipv6tuple; /*! \brief An IPv4 server address. */ typedef struct ipv4serverinfo { uint32_t m_ip; ///< address uint16_t m_port; ///< port uint8_t m_l4proto; ///< IP protocol } ipv4serverinfo; /*! \brief An IPv6 server address. */ typedef struct ipv6serverinfo { ipv6addr m_ip; ///< address uint16_t m_port; ///< port uint8_t m_l4proto; ///< IP protocol } ipv6serverinfo; /*! \brief A unix socket tuple. */ typedef union _unix_tuple { struct { uint64_t m_source; ///< source OS pointer. uint64_t m_dest; ///< destination OS pointer. } m_fields; uint8_t m_all[16]; ///< The fields as a raw array ob bytes. Used for hasing. } unix_tuple; /*@}*/ sysdig-0.26.4/userspace/libsinsp/uri.cpp000066400000000000000000000150151352731327100202330ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // uri.cpp // // URI utility // #include "uri.h" #include "sinsp.h" #include #include const std::string uri::SPECIAL_CHARS = "!#$&'()*+,/:;=?@[]"; const std::string uri::AMBIGUOUS_CHARS = " \"%-.<>\\^_`{|}~"; uri::uri(std::string str) { trim(str); // parser does not handle missing host properly bool no_host = false; if(ci_find_substr(str, std::string("file:///")) == 0) { str.insert(7, "localhost"); no_host = true; } parsed_uri p_uri = parse_uri(str.c_str()); if(p_uri.error) { str = std::string("Invalid URI: [").append(str).append(1, ']'); throw sinsp_exception(str); } m_scheme = str.substr(p_uri.scheme_start, p_uri.scheme_end - p_uri.scheme_start); std::transform(m_scheme.begin(), m_scheme.end(), m_scheme.begin(), ::tolower); if(!no_host) { m_host = str.substr(p_uri.host_start, p_uri.host_end - p_uri.host_start); std::transform(m_host.begin(), m_host.end(), m_host.begin(), ::tolower); } m_port = p_uri.port; if(m_port == 0) { m_has_port = false; m_port = get_well_known_port(); } m_path = str.substr(p_uri.path_start, p_uri.path_end - p_uri.path_start); m_query = str.substr(p_uri.query_start, p_uri.query_end - p_uri.query_start); if(p_uri.user_info_end != p_uri.user_info_start) { std::string auth = str.substr(p_uri.user_info_start, p_uri.user_info_end - p_uri.user_info_start); std::string::size_type pos = auth.find(':'); if(pos == std::string::npos) { throw sinsp_exception("Invalid credentials format."); } m_user = auth.substr(0, pos); m_password = auth.substr(pos + 1); } } void uri::check(std::string str) { trim(str); // parser does not handle missing host properly if(ci_find_substr(str, std::string("file:///")) == 0) { str.insert(7, "localhost"); } parsed_uri p_uri = parse_uri(str.c_str()); if(p_uri.error) { str = std::string("Invalid URI: [").append(str).append(1, ']'); throw sinsp_exception(str); } if(p_uri.user_info_end != p_uri.user_info_start) { std::string auth = str.substr(p_uri.user_info_start, p_uri.user_info_end - p_uri.user_info_start); std::string::size_type pos = auth.find(':'); if(pos == std::string::npos) { throw sinsp_exception("Invalid credentials format."); } } } int uri::get_well_known_port() const { if (!m_scheme.empty()) { if(m_scheme == "http") { return 80; } else if(m_scheme == "file") { return 0; } else if(m_scheme == "https") { return 443; } else if(m_scheme == "ftp") { return 21; } else if(m_scheme == "ssh") { return 22; } else if(m_scheme == "telnet") { return 23; } else if(m_scheme == "nntp") { return 119; } else if(m_scheme == "ldap") { return 389; } else if(m_scheme == "rtsp") { return 554; } else if(m_scheme == "sip") { return 5060; } else if(m_scheme == "sips") { return 5061; } else if(m_scheme == "xmpp") { return 5222; } } return 0; } void uri::set_path(const std::string& path) { uri u(*this); u.m_path = path; parsed_uri p_uri = parse_uri(u.to_string().c_str()); if(p_uri.error) { throw sinsp_exception(std::string("Invalid URI Path: [").append(path).append(1, ']')); } m_path = path; } std::string uri::to_string(bool show_creds) const { std::ostringstream ostr; ostr << m_scheme << "://"; if(!m_user.empty()) { if(show_creds) { ostr << m_user << ':' << m_password << '@'; } else { ostr << "***:***@"; } } ostr << m_host; if(m_port && m_has_port) { ostr << ':' << m_port; } ostr << m_path; if(!m_query.empty()) { ostr << '?' << m_query; } return ostr.str(); } std::string uri::encode(const std::string& str, const std::string& reserved) { std::string encoded_str; for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { char c = *it; if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) { encoded_str += c; } else if (c <= 0x20 || c >= 0x7F || SPECIAL_CHARS.find(c) != std::string::npos || AMBIGUOUS_CHARS.find(c) != std::string::npos || reserved.find(c) != std::string::npos) { std::ostringstream ostr; ostr << "%" << std::setfill('0') << std::setw(2) << std::uppercase << std::hex << ((unsigned) (unsigned char) c); encoded_str.append(ostr.str()); } else { encoded_str += c; } } return encoded_str; } // URI-decodes the given string by replacing percent-encoded // characters with the actual character. Returns the decoded string. // // When plus_as_space is true, non-encoded plus signs in the query are decoded as spaces. // (http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1) std::string uri::decode(const std::string& str, bool plus_as_space) { std::string decoded_str; bool in_query = false; std::string::const_iterator it = str.begin(); std::string::const_iterator end = str.end(); while(it != end) { char c = *it++; if(c == '?') { in_query = true; } // spaces may be encoded as plus signs in the query if(in_query && plus_as_space && c == '+') { c = ' '; } else if(c == '%') { if (it == end) { throw sinsp_exception("URI encoding: no hex digit following percent sign in " + str); } char hi = *it++; if (it == end) { throw sinsp_exception("URI encoding: two hex digits must follow percent sign in " + str); } char lo = *it++; if (hi >= '0' && hi <= '9') { c = hi - '0'; } else if (hi >= 'A' && hi <= 'F') { c = hi - 'A' + 10; } else if (hi >= 'a' && hi <= 'f') { c = hi - 'a' + 10; } else { throw sinsp_exception("URI encoding: not a hex digit found in " + str); } c *= 16; if (lo >= '0' && lo <= '9') { c += lo - '0'; } else if (lo >= 'A' && lo <= 'F') { c += lo - 'A' + 10; } else if (lo >= 'a' && lo <= 'f') { c += lo - 'a' + 10; } else { throw sinsp_exception("URI encoding: not a hex digit"); } } decoded_str += c; } return decoded_str; } bool uri::is(const std::string& proto) { return ci_compare::is_equal(m_scheme, proto); } sysdig-0.26.4/userspace/libsinsp/uri.h000066400000000000000000000073451352731327100177070ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // uri.h // // URI utility // #pragma once #ifdef _WIN32 #pragma warning(disable: 4190) #endif #include "uri_parser.h" #include // TODO: support fragments class uri { public: typedef std::pair credentials_t; static const std::string SPECIAL_CHARS; static const std::string AMBIGUOUS_CHARS; uri() = delete; uri(std::string str); const std::string& get_scheme() const; const std::string& get_user() const; const std::string& get_password() const; const std::string& get_host() const; const std::string& get_path() const; void set_path(const std::string& path); const std::string& get_query() const; int get_port() const; void set_scheme(std::string scheme); void set_host(std::string host); bool is(const std::string& proto); bool is_file() const; bool is_secure() const; std::string get_credentials() const; credentials_t& get_credentials(credentials_t& creds) const; void set_credentials(const credentials_t& cred); std::string to_string(bool show_creds = true) const; bool is_local() const; // URI-encodes the given string by escaping reserved, ambiguous and non-ASCII // characters. Returns the encoded string with uppercase hex letters (eg. %5B, not %5b). static std::string encode(const std::string& str, const std::string& reserved = ""); // URI-decodes the given string by replacing percent-encoded // characters with the actual character. Returns the decoded string. // // When plus_as_space is true, non-encoded plus signs in the query are decoded as spaces. // (http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1) static std::string decode(const std::string& str, bool plus_as_space = false); static void check(std::string str); private: int get_well_known_port() const; std::string m_scheme, m_user, m_password, m_host, m_path, m_query; int m_port; bool m_has_port = true; }; inline const std::string& uri::get_scheme() const { return m_scheme; } inline void uri::set_scheme(std::string scheme) { m_scheme = move(scheme); } inline const std::string& uri::get_user() const { return m_user; } inline const std::string& uri::get_password() const { return m_password; } inline const std::string& uri::get_host() const { return m_host; } inline void uri::set_host(std::string host) { m_host = move(host); } inline const std::string& uri::get_path() const { return m_path; } inline const std::string& uri::get_query() const { return m_query; } inline int uri::get_port() const { return m_port; } inline bool uri::is_file() const { return m_scheme == "file"; } inline bool uri::is_secure() const { return m_scheme == "https"; } inline void uri::set_credentials(const credentials_t& cred) { m_user = cred.first; m_password = cred.second; } inline std::string uri::get_credentials() const { std::string creds; if(!m_user.empty()) { creds.append(m_user).append(1, ':').append(m_password); } return creds; } inline uri::credentials_t& uri::get_credentials(credentials_t& creds) const { creds.first = m_user; creds.second = m_password; return creds; } inline bool uri::is_local() const { return m_host == "localhost" || m_host == "127.0.0.1" || m_scheme == "file"; } sysdig-0.26.4/userspace/libsinsp/uri_parser.c000066400000000000000000000410551352731327100212520ustar00rootroot00000000000000/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev * * Additional changes are licensed under the same terms as NGINX and * copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "uri_parser.h" #include #include #include #include #ifndef BIT_AT # define BIT_AT(a, i) \ (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ (1 << ((unsigned int) (i) & 7)))) #endif #if HTTP_PARSER_STRICT # define T(v) 0 #else # define T(v) v #endif static const uint8_t normal_url_char[32] = { /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; #undef T enum state { s_dead = 1 /* important that this is > 0 */ , s_req_spaces_before_url , s_req_schema , s_req_schema_slash , s_req_schema_slash_slash , s_req_server_start , s_req_server , s_req_server_with_at , s_req_path , s_req_query_string_start , s_req_query_string , s_req_fragment_start , s_req_fragment }; enum http_host_state { s_http_host_dead = 1 , s_http_userinfo_start , s_http_userinfo , s_http_host_start , s_http_host_v6_start , s_http_host , s_http_host_v6 , s_http_host_v6_end , s_http_host_v6_zone_start , s_http_host_v6_zone , s_http_host_port_start , s_http_host_port }; /* Macros for character classes; depends on strict-mode */ #define CR '\r' #define LF '\n' #define LOWER(c) (unsigned char)(c | 0x20) #define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') #define IS_NUM(c) ((c) >= '0' && (c) <= '9') #define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) #define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) #define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ (c) == ')') #define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ (c) == '$' || (c) == ',') #define STRICT_TOKEN(c) (tokens[(unsigned char)c]) #if HTTP_PARSER_STRICT #define TOKEN(c) (tokens[(unsigned char)c]) #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) #define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') #else #define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) #define IS_URL_CHAR(c) \ (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) #define IS_HOST_CHAR(c) \ (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') #endif #if HTTP_PARSER_STRICT # define STRICT_CHECK(cond) \ do { \ if (cond) { \ SET_ERRNO(HPE_STRICT); \ goto error; \ } \ } while (0) # define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) #else # define STRICT_CHECK(cond) # define NEW_MESSAGE() start_state #endif /* Our URL parser. * * This is designed to be shared by http_parser_execute() for URL validation, * hence it has a state transition + byte-for-byte interface. In addition, it * is meant to be embedded in http_parser_parse_uri(), which does the dirty * work of turning state transitions URL components for its API. * * This function should only be invoked with non-space characters. It is * assumed that the caller cares about (and can detect) the transition between * URL and non-URL states by looking for these. */ static enum state parse_url_char(enum state s, const char ch) { if (ch == ' ' || ch == '\r' || ch == '\n') { return s_dead; } #if HTTP_PARSER_STRICT if (ch == '\t' || ch == '\f') { return s_dead; } #endif switch (s) { case s_req_spaces_before_url: /* Proxied requests are followed by scheme of an absolute URI (alpha). * All methods except CONNECT are followed by '/' or '*'. */ if (ch == '/' || ch == '*') { return s_req_path; } if (IS_ALPHA(ch)) { return s_req_schema; } break; case s_req_schema: if (IS_ALPHA(ch)) { return s; } if (ch == ':') { return s_req_schema_slash; } break; case s_req_schema_slash: if (ch == '/') { return s_req_schema_slash_slash; } break; case s_req_schema_slash_slash: if (ch == '/') { return s_req_server_start; } break; case s_req_server_with_at: if (ch == '@') { return s_dead; } /* FALLTHROUGH */ case s_req_server_start: case s_req_server: if (ch == '/') { return s_req_path; } if (ch == '?') { return s_req_query_string_start; } if (ch == '@') { return s_req_server_with_at; } if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { return s_req_server; } break; case s_req_path: if (IS_URL_CHAR(ch)) { return s; } switch (ch) { case '?': return s_req_query_string_start; case '#': return s_req_fragment_start; } break; case s_req_query_string_start: case s_req_query_string: if (IS_URL_CHAR(ch)) { return s_req_query_string; } switch (ch) { case '?': /* allow extra '?' in query string */ return s_req_query_string; case '#': return s_req_fragment_start; } break; case s_req_fragment_start: if (IS_URL_CHAR(ch)) { return s_req_fragment; } switch (ch) { case '?': return s_req_fragment; case '#': return s; } break; case s_req_fragment: if (IS_URL_CHAR(ch)) { return s; } switch (ch) { case '?': case '#': return s; } break; default: break; } /* We should never fall out of the switch above unless there's an error */ return s_dead; } static enum http_host_state http_parse_host_char(enum http_host_state s, const char ch) { switch(s) { case s_http_userinfo: case s_http_userinfo_start: if (ch == '@') { return s_http_host_start; } if (IS_USERINFO_CHAR(ch)) { return s_http_userinfo; } break; case s_http_host_start: if (ch == '[') { return s_http_host_v6_start; } if (IS_HOST_CHAR(ch)) { return s_http_host; } break; case s_http_host: if (IS_HOST_CHAR(ch)) { return s_http_host; } /* FALLTHROUGH */ case s_http_host_v6_end: if (ch == ':') { return s_http_host_port_start; } break; case s_http_host_v6: if (ch == ']') { return s_http_host_v6_end; } /* FALLTHROUGH */ case s_http_host_v6_start: if (IS_HEX(ch) || ch == ':' || ch == '.') { return s_http_host_v6; } if (s == s_http_host_v6 && ch == '%') { return s_http_host_v6_zone_start; } break; case s_http_host_v6_zone: if (ch == ']') { return s_http_host_v6_end; } /* FALLTHROUGH */ case s_http_host_v6_zone_start: /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */ if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' || ch == '~') { return s_http_host_v6_zone; } break; case s_http_host_port: case s_http_host_port_start: if (IS_NUM(ch)) { return s_http_host_port; } break; default: break; } return s_http_host_dead; } static int http_parse_host(const char * buf, struct http_parser_uri *u, int found_at) { assert(u->field_set & (1 << URI_FLD_HOST)); enum http_host_state s; const char *p; size_t buflen = u->field_data[URI_FLD_HOST].off + u->field_data[URI_FLD_HOST].len; u->field_data[URI_FLD_HOST].len = 0; s = found_at ? s_http_userinfo_start : s_http_host_start; for (p = buf + u->field_data[URI_FLD_HOST].off; p < buf + buflen; p++) { enum http_host_state new_s = http_parse_host_char(s, *p); if (new_s == s_http_host_dead) { return 1; } switch(new_s) { case s_http_host: if (s != s_http_host) { u->field_data[URI_FLD_HOST].off = p - buf; } u->field_data[URI_FLD_HOST].len++; break; case s_http_host_v6: if (s != s_http_host_v6) { u->field_data[URI_FLD_HOST].off = p - buf; } u->field_data[URI_FLD_HOST].len++; break; case s_http_host_v6_zone_start: case s_http_host_v6_zone: u->field_data[URI_FLD_HOST].len++; break; case s_http_host_port: if (s != s_http_host_port) { u->field_data[URI_FLD_PORT].off = p - buf; u->field_data[URI_FLD_PORT].len = 0; u->field_set |= (1 << URI_FLD_PORT); } u->field_data[URI_FLD_PORT].len++; break; case s_http_userinfo: if (s != s_http_userinfo) { u->field_data[URI_FLD_USERINFO].off = p - buf ; u->field_data[URI_FLD_USERINFO].len = 0; u->field_set |= (1 << URI_FLD_USERINFO); } u->field_data[URI_FLD_USERINFO].len++; break; default: break; } s = new_s; } /* Make sure we don't end somewhere unexpected */ switch (s) { case s_http_host_start: case s_http_host_v6_start: case s_http_host_v6: case s_http_host_v6_zone_start: case s_http_host_v6_zone: case s_http_host_port_start: case s_http_userinfo: case s_http_userinfo_start: return 1; default: break; } return 0; } void http_parser_uri_init(struct http_parser_uri *u) { memset(u, 0, sizeof(*u)); } int http_parser_parse_uri(const char *buf, size_t buflen, int is_connect, struct http_parser_uri *u) { enum state s; const char *p; enum http_parser_uri_fields uf, old_uf; int found_at = 0; u->port = u->field_set = 0; s = is_connect ? s_req_server_start : s_req_spaces_before_url; old_uf = URI_FLD_MAX; for (p = buf; p < buf + buflen; p++) { s = parse_url_char(s, *p); /* Figure out the next field that we're operating on */ switch (s) { case s_dead: return 1; /* Skip delimiters */ case s_req_schema_slash: case s_req_schema_slash_slash: case s_req_server_start: case s_req_query_string_start: case s_req_fragment_start: continue; case s_req_schema: uf = URI_FLD_SCHEMA; break; case s_req_server_with_at: found_at = 1; /* FALLTROUGH */ case s_req_server: uf = URI_FLD_HOST; break; case s_req_path: uf = URI_FLD_PATH; break; case s_req_query_string: uf = URI_FLD_QUERY; break; case s_req_fragment: uf = URI_FLD_FRAGMENT; break; default: assert(!"Unexpected state"); return 1; } /* Nothing's changed; soldier on */ if (uf == old_uf) { u->field_data[uf].len++; continue; } u->field_data[uf].off = p - buf; u->field_data[uf].len = 1; u->field_set |= (1 << uf); old_uf = uf; } /* host must be present if there is a schema */ /* parsing http:///toto will fail */ if ((u->field_set & (1 << URI_FLD_SCHEMA)) && (u->field_set & (1 << URI_FLD_HOST)) == 0) { return 1; } if (u->field_set & (1 << URI_FLD_HOST)) { if (http_parse_host(buf, u, found_at) != 0) { return 1; } } /* CONNECT requests can only contain "hostname:port" */ if (is_connect && u->field_set != ((1 << URI_FLD_HOST)|(1 << URI_FLD_PORT))) { return 1; } if (u->field_set & (1 << URI_FLD_PORT)) { /* Don't bother with endp; we've already validated the string */ unsigned long v = strtoul(buf + u->field_data[URI_FLD_PORT].off, NULL, 10); /* Ports have a max value of 2^16 */ if (v > 0xffff) { return 1; } u->port = (uint16_t) v; } return 0; } struct parsed_uri parse_uri(const char *uri_string) { struct http_parser_uri u; http_parser_uri_init(&u); int rc = http_parser_parse_uri(uri_string, strlen(uri_string), 0, &u); if (rc) { struct parsed_uri uri = {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; return uri; } struct parsed_uri uri = { 0, u.field_set, u.field_data[0].off, u.field_data[0].off + u.field_data[0].len, u.field_data[6].off, u.field_data[6].off + u.field_data[6].len, u.field_data[1].off, u.field_data[1].off + u.field_data[1].len, u.port, u.field_data[3].off, u.field_data[3].off + u.field_data[3].len, u.field_data[4].off, u.field_data[4].off + u.field_data[4].len, u.field_data[5].off, u.field_data[5].off + u.field_data[5].len, }; return uri; } sysdig-0.26.4/userspace/libsinsp/uri_parser.h000066400000000000000000000072441352731327100212610ustar00rootroot00000000000000/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #ifndef uri_parser_h #define uri_parser_h #ifdef __cplusplus extern "C" { #endif #include #if defined(_WIN32) && !defined(__MINGW32__) && \ (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__) #include #include typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int16 int16_t; typedef unsigned __int16 uint16_t; typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #else #include #endif /* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run * faster */ #ifndef HTTP_PARSER_STRICT # define HTTP_PARSER_STRICT 1 #endif /* Get an http_errno value from an http_parser */ #define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) enum http_parser_uri_fields { URI_FLD_SCHEMA = 0 , URI_FLD_HOST = 1 , URI_FLD_PORT = 2 , URI_FLD_PATH = 3 , URI_FLD_QUERY = 4 , URI_FLD_FRAGMENT = 5 , URI_FLD_USERINFO = 6 , URI_FLD_MAX = 7 }; /* Result structure for http_parser_parse_uri(). * * Callers should index into field_data[] with UF_* values iff field_set * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and * because we probably have padding left over), we convert any port to * a uint16_t. */ struct http_parser_uri { uint16_t field_set; /* Bitmask of (1 << UF_*) values */ uint16_t port; /* Converted URI_FLD_PORT string */ struct { uint16_t off; /* Offset into buffer in which field starts */ uint16_t len; /* Length of run in buffer */ } field_data[URI_FLD_MAX]; }; /* Initialize all http_parser_uri members to 0 */ void http_parser_uri_init(struct http_parser_uri *u); /* Parse a URL; return nonzero on failure */ int http_parser_parse_uri(const char *buf, size_t buflen, int is_connect, struct http_parser_uri *u); struct parsed_uri { const uint8_t error; const uint16_t field_set; const uint16_t scheme_start; const uint16_t scheme_end; const uint16_t user_info_start; const uint16_t user_info_end; const uint16_t host_start; const uint16_t host_end; const unsigned short port; const uint16_t path_start; const uint16_t path_end; const uint16_t query_start; const uint16_t query_end; const uint16_t fragment_start; const uint16_t fragment_end; }; struct parsed_uri parse_uri(const char *uri_string); #ifdef __cplusplus } #endif #endif sysdig-0.26.4/userspace/libsinsp/user_event.cpp000066400000000000000000000243761352731327100216250ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "sinsp.h" #include "sinsp_int.h" #include "user_event.h" #include "user_event_logger.h" // // event_scope // const std::string event_scope::SCOPE_OP_AND = "and"; // these string lists contain reserved strings; some of the reserved // strings are escaped and mandatory to be first in RESERVED_STRINGS // and have their escaped counterparts in the REPLACEMENT_STRINGS, // in the same order as they appear in RESERVED_STRINGS const event_scope::string_list_t event_scope::RESERVED_STRINGS = {"'"}; const event_scope::string_list_t event_scope::REPLACEMENT_STRINGS = {"\\'"}; // scope key name format regex #ifndef _WIN32 const std::string event_scope::KEY_FORMAT = "[a-zA-Z0-9_/\\.-]*"; #else const std::string event_scope::KEY_FORMAT = "^[a-zA-Z0-9_/\\.-]*$"; #endif // _WIN32 event_scope::event_scope(const std::string& key, const std::string& value) { if(!key.empty() && !value.empty()) { add(key, value, ""); } } bool event_scope::add(const std::string& key, const std::string& value, const std::string& op) { if(check_key_format(key)) { std::string k(key); std::string o(!m_scope.empty() ? op : ""); std::string v(value); replace(v); if(!v.empty()) { if(!o.empty()) { m_scope.append(1, ' ').append(trim(o)).append(1, ' '); } m_scope.append(trim(k)).append("='").append(trim(v)).append(1, 0x27); return true; } } else { g_logger.log("Scope key is invalid: [" + key + "], entry will not be added to scope.", sinsp_logger::SEV_WARNING); } return false; } string& event_scope::replace(std::string& value) { ASSERT(RESERVED_STRINGS.size() == REPLACEMENT_STRINGS.size()); string_list_t::const_iterator res_it = RESERVED_STRINGS.cbegin(); string_list_t::const_iterator res_end = RESERVED_STRINGS.cend(); string_list_t::const_iterator rep_it = REPLACEMENT_STRINGS.cbegin(); string_list_t::const_iterator rep_end = REPLACEMENT_STRINGS.cend(); for(; res_it != res_end && rep_it != rep_end; ++res_it, ++rep_it) { replace_in_place(value, *res_it, *rep_it); } return value; } #ifndef _WIN32 void event_scope::regex_error(const std::string& call, size_t ret, regex_t* preg, const std::string& str) { if(!preg) { return; } char errbuf[256] = {0}; if(regerror(ret, preg, errbuf, 256)) { g_logger.log(call + "() error: " + errbuf, sinsp_logger::SEV_WARNING); } else { g_logger.log("Can't obtain " + call + "() [" + str + "] error.", sinsp_logger::SEV_WARNING); } } bool event_scope::check_key_format(const std::string& key) { if(key.empty()) { return false; } bool result = false; std::string exp(KEY_FORMAT); regex_t reg = {0}; size_t ret = regcomp(®, exp.c_str(), REG_EXTENDED); if(0 == ret) { regmatch_t rm = {0}; ret = regexec(®, key.c_str(), 1, &rm, 0); if(0 == ret) { if((rm.rm_eo - rm.rm_so) == static_cast(key.length())) { result = true; } } else { regex_error("regexec", ret, ®, key); } } else { regex_error("regcomp", ret, ®, exp); } regfree(®); return result; } #else bool event_scope::check_key_format(const std::string& key) { static const std::regex r(KEY_FORMAT); if (std::regex_match(key, r)) { return true; } return false; } #endif // _WIN32 // // user_event_meta_t // const std::string user_event_meta_t::PERMIT_ALL = "all"; user_event_meta_t::user_event_meta_t(const std::string& kind, const type_list_t& types): m_kind(kind), m_types(types) { } user_event_meta_t::user_event_meta_t(std::string&& kind, type_list_t&& types): m_kind(std::move(kind)), m_types(std::move(types)) { } user_event_meta_t::user_event_meta_t(const user_event_meta_t& other): m_kind(other.m_kind), m_types(other.m_types) { } user_event_meta_t::user_event_meta_t(user_event_meta_t&& other): m_kind(std::move(other.m_kind)), m_types(std::move(other.m_types)) { other.m_kind.clear(); other.m_types.clear(); } user_event_meta_t& user_event_meta_t::operator = (const user_event_meta_t& other) { if(&other != this) { m_kind = other.m_kind; m_types = other.m_types; } return *this; } user_event_meta_t& user_event_meta_t::operator = (user_event_meta_t&& other) { if(&other != this) { m_kind = std::move(other.m_kind); m_types = std::move(other.m_types); other.m_kind.clear(); other.m_types.clear(); } return *this; } // // user_event_filter_t // user_event_filter_t::user_event_filter_t() { } user_event_filter_t::user_event_filter_t(const list_t& list): m_list(list) { } user_event_filter_t::user_event_filter_t(list_t&& list): m_list(std::move(list)) { } void user_event_filter_t::add(const user_event_meta_t& evt) { if(handle_all(user_event_meta_t(evt))) { return; } if(get_meta(evt.kind()) == m_list.end()) { m_list.insert(evt); } } void user_event_filter_t::add(user_event_meta_t&& evt) { if(handle_all(user_event_meta_t(evt))) { return; } if(get_meta(evt.kind()) == m_list.end()) { m_list.emplace(std::move(evt)); } } bool user_event_filter_t::handle_all(user_event_meta_t&& evt) { if(ci_compare_str(evt.kind(), user_event_meta_t::PERMIT_ALL)) { m_list.clear(); m_list.emplace(std::move(evt)); return true; } user_event_meta_t loc_evt(evt); list_t::iterator it = m_list.find(loc_evt); if(it != m_list.end()) { if(it->types().find(user_event_meta_t::PERMIT_ALL) != it->types().end()) { m_list.erase(it); m_list.insert(loc_evt); return true; } } return false; } void user_event_filter_t::remove(const user_event_meta_t& evt) { m_list.erase(evt); } void user_event_filter_t::remove(const std::string& kind) { user_event_meta_t::type_list_t types; user_event_meta_t evt(kind, types); remove(evt); } user_event_filter_t::list_t::const_iterator user_event_filter_t::get(const std::string& evt_kind) const { list_t::const_iterator it = m_list.begin(), end = m_list.end(); for(;it != end; ++it) { if(ci_compare_str(it->kind(), evt_kind)) { return it; } } return end; } bool user_event_filter_t::has(const std::string& evt_kind, const std::string& evt_type) const { list_t::const_iterator it = get(evt_kind); if(it != m_list.end()) { for(const auto& t : it->types()) { if(ci_compare_str(t, evt_type)) { return true; } } } return false; } bool user_event_filter_t::allows(const user_event_meta_t& evt) const { list_t::const_iterator it = get_meta(evt.kind()); if(it != m_list.end()) // this event kind is allowed { // check for "any event" type being allowed by this filter if(it->types().find(user_event_meta_t::PERMIT_ALL) != it->types().end()) { return true; } // check if event has more types than this filter if(evt.types().size() > it->types().size()) { return false; } // if all event types are present in this filter, event is allowed for(auto const& type : evt.types()) { if(it->types().find(type) == it->types().end()) { return false; } } return true; } return false; } user_event_filter_t::list_t::const_iterator user_event_filter_t::get_meta(const std::string& evt_kind) const { list_t::const_iterator it = m_list.begin(), end = m_list.end(); for(; it != end; ++it) { if(ci_compare_str(it->kind(), user_event_meta_t::PERMIT_ALL) || ci_compare_str(it->kind(),evt_kind)) { return it; } } return end; } std::string user_event_filter_t::to_string() const { std::string ret; for(const auto& evt : m_list) { if(evt.types().size()) { ret.append(1, '\n').append(evt.kind()).append(1, ':'); for(const auto& type : evt.types()) { ret.append(type).append(", "); } } } return ret; } // // sinsp_user_event // sinsp_user_event::sinsp_user_event() : m_epoch_time_s(0), m_severity(~0) { } sinsp_user_event::sinsp_user_event(uint64_t epoch_time_s, string&& name, string&& desc, string&& scope, tag_map_t&& tags, uint32_t sev): m_epoch_time_s(epoch_time_s), m_name(std::move(name)), m_description(std::move(desc)), m_severity(sev), m_scope(std::move(scope)), m_tags(std::move(tags)) { } sinsp_user_event::sinsp_user_event(sinsp_user_event&& other): m_epoch_time_s(other.m_epoch_time_s), m_name(std::move(other.m_name)), m_description(std::move(other.m_description)), m_severity(other.m_severity), m_scope(std::move(other.m_scope)), m_tags(std::move(other.m_tags)) { } sinsp_user_event& sinsp_user_event::operator=(sinsp_user_event&& other) { if(this != &other) { m_epoch_time_s = other.m_epoch_time_s; m_name = std::move(other.m_name); m_description = std::move(other.m_description); m_severity = other.m_severity; m_scope = std::move(other.m_scope); m_tags = std::move(other.m_tags); } return *this; } std::string sinsp_user_event::to_string() { std::ostringstream ostr; ostr << "timestamp: " << m_epoch_time_s << '\n' << "name: " << m_name << "\n" "description: " << m_description << "\"\n" "scope: " << m_scope << "\"\n"; if(m_severity != UNKNOWN_SEVERITY) { ostr << "priority: " << m_severity << '\n'; } if(m_tags.size()) { ostr << "tags:"; for(auto& tag : m_tags) { ostr << "\n " << tag.first << ": " << tag.second; } } ostr << std::flush; return ostr.str(); } void sinsp_user_event::emit_event_overflow(const std::string& component, const std::string& machine_id, const std::string& source) { std::string event_name = component; event_name.append(" Event Limit Exceeded"); std::ostringstream description; description << component << " event limit (" << max_events_per_cycle() << " per second) exceeded. Excess events were discarded."; std::string scope; if(machine_id.length()) { scope.append("host.mac=").append(machine_id); } tag_map_t tags{{"source", source}}; auto evt = sinsp_user_event( get_epoch_utc_seconds_now(), std::move(event_name), description.str(), std::move(scope), std::move(tags), user_event_logger::SEV_EVT_WARNING); user_event_logger::log(evt, user_event_logger::SEV_EVT_WARNING); } sysdig-0.26.4/userspace/libsinsp/user_event.h000066400000000000000000000222551352731327100212640ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include "sinsp_int.h" #include #include #include #include #include #include // c++ std regex is buggy on g++ 4.8 // so we use POSIX regex on non-windows #ifndef _WIN32 #include #else #include #endif #include "sinsp_int.h" // // scope utilities // class event_scope { public: typedef std::vector string_list_t; static const std::string SCOPE_OP_AND; static const string_list_t RESERVED_STRINGS; static const string_list_t REPLACEMENT_STRINGS; static const std::string KEY_FORMAT; event_scope(const std::string& key = "", const std::string& value = ""); bool add(const std::string& key, const std::string& value, const std::string& op = SCOPE_OP_AND); const std::string& get() const; std::string& get_ref(); void clear(); // utility function to check that a scope entry key is valid; // valid entries match KEY_FORMAT regular expression static bool check_key_format(const std::string& key); private: // utility function to replace RESERVED_STRINGS with their // counterparts in REPLACEMENT_STRINGS static string& replace(std::string& scope); #ifndef _WIN32 static void regex_error(const std::string& call, size_t ret, regex_t* preg, const std::string& str); #endif std::string m_scope; }; inline const std::string& event_scope::get() const { if(m_scope.empty()) { g_logger.log("Scope is empty--at least one key/value pair should be present", sinsp_logger::SEV_WARNING); } return m_scope; } inline std::string& event_scope::get_ref() { if(m_scope.empty()) { g_logger.log("Scope is empty--at least one key/value pair should be present", sinsp_logger::SEV_WARNING); } return m_scope; } inline void event_scope::clear() { m_scope.clear(); } // // user-configured event meta // class user_event_meta_t { public: typedef std::set type_list_t; static const std::string PERMIT_ALL; user_event_meta_t() {}; ~user_event_meta_t() {}; user_event_meta_t(const std::string& kind, const type_list_t& types); user_event_meta_t(std::string&& kind, type_list_t&& types); user_event_meta_t(const user_event_meta_t& other); user_event_meta_t(user_event_meta_t&& other); user_event_meta_t& operator = (const user_event_meta_t& other); user_event_meta_t& operator = (user_event_meta_t&& other); bool operator < (const user_event_meta_t& other) const; const std::string& kind() const; const type_list_t& types() const; bool has_type(const std::string& type) const; bool any_type() const; bool is_kind(const std::string& kind) const; bool any_kind() const; private: std::string m_kind; type_list_t m_types; }; inline bool user_event_meta_t::operator < (const user_event_meta_t& other) const { #ifndef _WIN32 return strcasecmp(m_kind.c_str(), other.m_kind.c_str()) < 0; #else return lstrcmpiA(m_kind.c_str(), other.m_kind.c_str()) < 0; #endif // _WIN32 } inline const std::string& user_event_meta_t::kind() const { return m_kind; } inline const user_event_meta_t::type_list_t& user_event_meta_t::types() const { return m_types; } inline bool user_event_meta_t::has_type(const std::string& type) const { return m_types.find(type) != m_types.end() || any_type(); } inline bool user_event_meta_t::any_type() const { return m_types.find(PERMIT_ALL) != m_types.end(); } inline bool user_event_meta_t::is_kind(const std::string& kind) const { #ifndef _WIN32 return (strcasecmp(m_kind.c_str(), kind.c_str()) == 0) || any_kind(); #else return lstrcmpiA(m_kind.c_str(), kind.c_str()) < 0; #endif // _WIN32 } inline bool user_event_meta_t::any_kind() const { #ifndef _WIN32 return strcasecmp(m_kind.c_str(), PERMIT_ALL.c_str()) == 0; #else return lstrcmpiA(m_kind.c_str(), PERMIT_ALL.c_str()) < 0; #endif // _WIN32 } // // user-configured-event filter // class user_event_filter_t { public: typedef std::set list_t; typedef std::shared_ptr ptr_t; user_event_filter_t(); user_event_filter_t(const list_t& list); user_event_filter_t(list_t&& list); void add(const user_event_meta_t& evt); void add(user_event_meta_t&& evt); bool has(const std::string& evt_kind) const; bool has(const std::string& evt_kind, const std::string& evt_type) const; list_t::const_iterator get(const std::string& evt_kind) const; void remove(const user_event_meta_t& evt); void remove(const std::string& kind); void clear(); bool allows(const user_event_meta_t& evt) const; bool allows_all(const std::string& kind) const; bool allows_all() const; std::string to_string() const; private: static bool ci_compare_str(const std::string& a, const std::string& b); // if the filter entry of the requested kind is found, returns const iterator pointing to it; // if "any event" entry is found, iterator pointing to it is returned; otherwise, the iterator // pointing to the end of the filter list is returned (indicating the event kind was not found) list_t::const_iterator get_meta(const std::string& evt_kind) const; bool handle_all(user_event_meta_t&& evt); list_t m_list; }; inline void user_event_filter_t::clear() { m_list.clear(); } inline bool user_event_filter_t::ci_compare_str(const std::string& a, const std::string& b) { #ifndef _WIN32 return strcasecmp(a.c_str(), b.c_str()) == 0; #else return lstrcmpiA(a.c_str(), b.c_str()) == 0; #endif // _WIN32 } inline bool user_event_filter_t::has(const std::string& evt_kind) const { return get(evt_kind) != m_list.end(); } inline bool user_event_filter_t:: allows_all(const std::string& kind) const { return allows(user_event_meta_t(kind, {user_event_meta_t::PERMIT_ALL})); } inline bool user_event_filter_t::allows_all() const { return get(user_event_meta_t::PERMIT_ALL) != m_list.end(); } // // Wrapper class for user-configured events // class sinsp_user_event { public: typedef std::unordered_map tag_map_t; static const uint32_t UNKNOWN_SEVERITY = static_cast(~0); sinsp_user_event(const sinsp_user_event&) = delete; sinsp_user_event& operator=(const sinsp_user_event& other) = delete; sinsp_user_event(); sinsp_user_event(uint64_t epoch_time_s, std::string&& name, std::string&& desc, std::string&& scope, tag_map_t&& tags, uint32_t sev); sinsp_user_event(sinsp_user_event&& other); sinsp_user_event& operator=(sinsp_user_event&& other); uint64_t epoch_time_s() const; const std::string& name() const; const std::string& description() const; uint32_t severity() const; const std::string& scope() const; const tag_map_t& tags() const; /** * \brief Format the event as a YAML-like human readable string * @return the formatted string * * Note: While the format looks superficially similar to YAML, it's not. * This method does not generate valid YAML, especially when characters * like quotes, backslashes or newlines are found in any of the fields */ std::string to_string(); static void emit_event_overflow(const std::string& component, const std::string& machine_id, const std::string& source = "sysdig-agent"); static size_t max_events_per_cycle(); private: uint64_t m_epoch_time_s; std::string m_name; std::string m_description; uint32_t m_severity; std::string m_scope; tag_map_t m_tags; }; inline uint64_t sinsp_user_event::epoch_time_s() const { return m_epoch_time_s; } inline const std::string& sinsp_user_event::name() const { return m_name; } inline const std::string& sinsp_user_event::description() const { return m_description; } inline uint32_t sinsp_user_event::severity() const { return m_severity; } inline const std::string& sinsp_user_event::scope() const { return m_scope; } inline const sinsp_user_event::tag_map_t& sinsp_user_event::tags() const { return m_tags; } inline size_t sinsp_user_event::max_events_per_cycle() { return 100u; // TODO: move this value to config? } // // User-configured events queue // class user_event_queue { public: typedef std::shared_ptr ptr_t; typedef std::deque type_t; void add(sinsp_user_event&& evt); bool get(sinsp_user_event& evt); type_t::size_type count() const; private: type_t m_queue; mutable std::mutex m_mutex; }; inline void user_event_queue::add(sinsp_user_event&& evt) { std::lock_guard lock(m_mutex); m_queue.emplace_back(std::move(evt)); } inline bool user_event_queue::get(sinsp_user_event& evt) { std::lock_guard lock(m_mutex); if(!m_queue.size()) { return false; } evt = std::move(m_queue.front()); m_queue.pop_front(); return true; } inline user_event_queue::type_t::size_type user_event_queue::count() const { std::lock_guard lock(m_mutex); return m_queue.size(); } sysdig-0.26.4/userspace/libsinsp/user_event_logger.cpp000066400000000000000000000030331352731327100231470ustar00rootroot00000000000000/* Copyright (C) 2019 Sysdig Inc. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "user_event_logger.h" #include #include namespace { /** * Do-nothing realization of the user_event_logger::callback interface. */ class null_callback : public user_event_logger::callback { public: void log(const sinsp_user_event& evt, const user_event_logger::severity severity) override { } bool is_null() const override { return true; } }; /** The current callback handler. */ user_event_logger::callback::ptr_t s_callback = std::make_shared(); } // end namespace void user_event_logger::log(const sinsp_user_event& evt, const user_event_logger::severity severity) { s_callback->log(evt, severity); } void user_event_logger::register_callback(callback::ptr_t callback) { if(callback) { s_callback = callback; } else { s_callback = std::make_shared(); } } const user_event_logger::callback& user_event_logger::get_callback() { return *s_callback; } sysdig-0.26.4/userspace/libsinsp/user_event_logger.h000066400000000000000000000043231352731327100226170ustar00rootroot00000000000000/* Copyright (C) 2019 Sysdig Inc. This file is part of sysdig. 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. */ #pragma once #include #include "user_event.h" /** * This namespace exposes an API for logging user events. */ namespace user_event_logger { /** * The severities at which user events may be logged. */ enum severity { SEV_EVT_FATAL, SEV_EVT_CRITICAL, SEV_EVT_ERROR, SEV_EVT_WARNING, SEV_EVT_NOTICE, SEV_EVT_INFORMATION, SEV_EVT_DEBUG, }; /** * Interface to an object that will receive callbacks whenever user event * logs are generated. */ class callback { public: using ptr_t = std::shared_ptr; virtual ~callback() = default; /** * Write the given log str with the given severity. */ virtual void log(const sinsp_user_event& evt, user_event_logger::severity sev) = 0; /** * We use the "Null Object Pattern" with this interface; this will * return true for do-nothing implementations, false otherwise. */ virtual bool is_null() const { return false; } }; /** * Write the given user event log message with the given severity to the * registered callback. */ void log(const sinsp_user_event& evt, user_event_logger::severity sev); /** * Register the given callback. If a callback is already registered, it will * be replaced with the given callback. The given callback may be nullptr, * in which case the registered callback will be replaced with a null * callback handler. */ void register_callback(user_event_logger::callback::ptr_t callback); /** * Returns a reference to the current callback handler. Use the is_null() * method on the returned object to determine if the handler is expected to * perform useful logging. */ const callback& get_callback(); } // end namespace user_event_logger sysdig-0.26.4/userspace/libsinsp/utils.cpp000066400000000000000000001032401352731327100205720ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #ifndef _WIN32 #include #include #include #include #ifndef CYGWING_AGENT #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 "sinsp.h" #include "sinsp_int.h" #include "sinsp_errno.h" #include "sinsp_signal.h" #include "filter.h" #include "filterchecks.h" #include "chisel.h" #include "protodecoder.h" #include "uri.h" #ifndef _WIN32 #include "curl/curl.h" #endif #ifndef PATH_MAX #define PATH_MAX 4096 #endif #ifdef HAS_CHISELS const chiseldir_info g_chisel_dirs_array[] = { {false, ""}, // file as is #ifdef _WIN32 {false, "c:/sysdig/chisels/"}, #endif {false, "./chisels/"}, {true, "~/.chisels/"}, }; #endif #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 resolved; } #endif /////////////////////////////////////////////////////////////////////////////// // sinsp_initializer implementation /////////////////////////////////////////////////////////////////////////////// // // These are the libsinsp globals // sinsp_evttables g_infotables; sinsp_logger g_logger; sinsp_initializer g_initializer; #ifdef HAS_FILTERING sinsp_filter_check_list g_filterlist; #endif sinsp_protodecoder_list g_decoderlist; #ifdef HAS_CHISELS vector* g_chisel_dirs = NULL; #endif // // loading time initializations // sinsp_initializer::sinsp_initializer() { // // Init the event tables // g_infotables.m_event_info = scap_get_event_info_table(); g_infotables.m_syscall_info_table = scap_get_syscall_info_table(); // // Init the logger // g_logger.set_severity(sinsp_logger::SEV_TRACE); #ifdef HAS_CHISELS // // 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]); } } #endif // HAS_CHISELS // // Sockets initialization on windows // #ifdef _WIN32 WSADATA wsaData; WORD version = MAKEWORD( 2, 0 ); WSAStartup( version, &wsaData ); #endif } sinsp_initializer::~sinsp_initializer() { #ifdef HAS_CHISELS if(g_chisel_dirs) { delete g_chisel_dirs; } #endif } /////////////////////////////////////////////////////////////////////////////// // Various helper functions /////////////////////////////////////////////////////////////////////////////// // // errno to string conversion. // Only the first 40 error codes are currently implemented // const char* sinsp_utils::errno_to_str(int32_t code) { switch(-code) { case SE_EPERM: return "EPERM"; case SE_ENOENT: return "ENOENT"; case SE_ESRCH: return "ESRCH"; case SE_EINTR: return "EINTR"; case SE_EIO: return "EIO"; case SE_ENXIO: return "ENXIO"; case SE_E2BIG: return "E2BIG"; case SE_ENOEXEC: return "ENOEXEC"; case SE_EBADF: return "EBADF"; case SE_ECHILD: return "ECHILD"; case SE_EAGAIN: return "EAGAIN"; case SE_ENOMEM: return "ENOMEM"; case SE_EACCES: return "EACCES"; case SE_EFAULT: return "EFAULT"; case SE_ENOTBLK: return "ENOTBLK"; case SE_EBUSY: return "EBUSY"; case SE_EEXIST: return "EEXIST"; case SE_EXDEV: return "EXDEV"; case SE_ENODEV: return "ENODEV"; case SE_ENOTDIR: return "ENOTDIR"; case SE_EISDIR: return "EISDIR"; case SE_EINVAL: return "EINVAL"; case SE_ENFILE: return "ENFILE"; case SE_EMFILE: return "EMFILE"; case SE_ENOTTY: return "ENOTTY"; case SE_ETXTBSY: return "ETXTBSY"; case SE_EFBIG: return "EFBIG"; case SE_ENOSPC: return "ENOSPC"; case SE_ESPIPE: return "ESPIPE"; case SE_EROFS: return "EROFS"; case SE_EMLINK: return "EMLINK"; case SE_EPIPE: return "EPIPE"; case SE_EDOM: return "EDOM"; case SE_ERANGE: return "ERANGE"; case SE_EDEADLK: return "EDEADLK"; case SE_ENAMETOOLONG: return "ENAMETOOLONG"; case SE_ENOLCK: return "ENOLCK"; case SE_ENOSYS: return "ENOSYS"; case SE_ENOTEMPTY: return "ENOTEMPTY"; case SE_ELOOP: return "ELOOP"; case SE_ERESTARTSYS: return "ERESTARTSYS"; case SE_ENETUNREACH: return "ENETUNREACH"; case SE_EINPROGRESS: return "EINPROGRESS"; case SE_ETIMEDOUT: return "ETIMEDOUT"; case SE_ECONNRESET: return "ECONNRESET"; case SE_ECONNREFUSED: return "ECONNREFUSED"; case SE_ERESTARTNOHAND: return "ERESTARTNOHAND"; case SE_EADDRNOTAVAIL: return "EADDRNOTAVAIL"; case SE_ENOTCONN: return "ENOTCONN"; case SE_ENETDOWN: return "ENETDOWN"; case SE_EOPNOTSUPP: return "EOPNOTSUPP"; case SE_ENOTSOCK: return "ENOTSOCK"; case SE_ERESTART_RESTARTBLOCK: return "ERESTART_RESTARTBLOCK"; case SE_EADDRINUSE: return "EADDRINUSE"; case SE_EPROTOTYPE: return "EPROTOTYPE"; case SE_EALREADY: return "EALREADY"; case SE_ENOMEDIUM: return "ENOMEDIUM"; case SE_ECANCELED: return "ECANCELED"; case SE_EPROTONOSUPPORT: return "EPROTONOSUPPORT"; default: ASSERT(false); return ""; } } // // signal to string conversion. // Only non-extremely-obscure signals are implemented // const char* sinsp_utils::signal_to_str(uint8_t code) { switch(code) { case SE_SIGHUP: return "SIGHUP"; case SE_SIGINT: return "SIGINT"; case SE_SIGQUIT: return "SIGQUIT"; case SE_SIGILL: return "SIGILL"; case SE_SIGTRAP: return "SIGTRAP"; case SE_SIGABRT: return "SIGABRT"; case SE_SIGBUS: return "SIGBUS"; case SE_SIGFPE: return "SIGFPE"; case SE_SIGKILL: return "SIGKILL"; case SE_SIGUSR1: return "SIGUSR1"; case SE_SIGSEGV: return "SIGSEGV"; case SE_SIGUSR2: return "SIGUSR2"; case SE_SIGPIPE: return "SIGPIPE"; case SE_SIGALRM: return "SIGALRM"; case SE_SIGTERM: return "SIGTERM"; case SE_SIGSTKFLT: return "SIGSTKFLT"; case SE_SIGCHLD: return "SIGCHLD"; case SE_SIGCONT: return "SIGCONT"; case SE_SIGSTOP: return "SIGSTOP"; case SE_SIGTSTP: return "SIGTSTP"; case SE_SIGTTIN: return "SIGTTIN"; case SE_SIGTTOU: return "SIGTTOU"; case SE_SIGURG: return "SIGURG"; case SE_SIGXCPU: return "SIGXCPU"; case SE_SIGXFSZ: return "SIGXFSZ"; case SE_SIGVTALRM: return "SIGVTALRM"; case SE_SIGPROF: return "SIGPROF"; case SE_SIGWINCH: return "SIGWINCH"; case SE_SIGIO: return "SIGIO"; case SE_SIGPWR: return "SIGPWR"; case SE_SIGSYS: return "SIGSYS"; default: return NULL; } } bool sinsp_utils::sockinfo_to_str(sinsp_sockinfo* sinfo, scap_fd_type stype, char* targetbuf, uint32_t targetbuf_size, bool resolve) { if(stype == SCAP_FD_IPV4_SOCK) { uint8_t* sb = (uint8_t*)&sinfo->m_ipv4info.m_fields.m_sip; uint8_t* db = (uint8_t*)&sinfo->m_ipv4info.m_fields.m_dip; if(sinfo->m_ipv4info.m_fields.m_l4proto == SCAP_L4_TCP || sinfo->m_ipv4info.m_fields.m_l4proto == SCAP_L4_UDP) { ipv4tuple addr; addr.m_fields.m_sip = *(uint32_t*)sb; addr.m_fields.m_sport = sinfo->m_ipv4info.m_fields.m_sport; addr.m_fields.m_dip = *(uint32_t*)db; addr.m_fields.m_dport = sinfo->m_ipv4info.m_fields.m_dport; addr.m_fields.m_l4proto = sinfo->m_ipv4info.m_fields.m_l4proto; string straddr = ipv4tuple_to_string(&addr, resolve); snprintf(targetbuf, targetbuf_size, "%s", straddr.c_str()); } else if(sinfo->m_ipv4info.m_fields.m_l4proto == SCAP_L4_ICMP || sinfo->m_ipv4info.m_fields.m_l4proto == SCAP_L4_RAW) { snprintf(targetbuf, targetbuf_size, "%u.%u.%u.%u->%u.%u.%u.%u", (unsigned int)(uint8_t)sb[0], (unsigned int)(uint8_t)sb[1], (unsigned int)(uint8_t)sb[2], (unsigned int)(uint8_t)sb[3], (unsigned int)(uint8_t)db[0], (unsigned int)(uint8_t)db[1], (unsigned int)(uint8_t)db[2], (unsigned int)(uint8_t)db[3]); } else { snprintf(targetbuf, targetbuf_size, ""); } } else if(stype == SCAP_FD_IPV6_SOCK) { uint8_t* sip6 = (uint8_t*)sinfo->m_ipv6info.m_fields.m_sip.m_b; uint8_t* dip6 = (uint8_t*)sinfo->m_ipv6info.m_fields.m_dip.m_b; uint8_t* sip = ((uint8_t*)(sinfo->m_ipv6info.m_fields.m_sip.m_b)) + 12; uint8_t* dip = ((uint8_t*)(sinfo->m_ipv6info.m_fields.m_dip.m_b)) + 12; if(sinfo->m_ipv6info.m_fields.m_l4proto == SCAP_L4_TCP || sinfo->m_ipv6info.m_fields.m_l4proto == SCAP_L4_UDP) { if(sinsp_utils::is_ipv4_mapped_ipv6(sip6) && sinsp_utils::is_ipv4_mapped_ipv6(dip6)) { ipv4tuple addr; addr.m_fields.m_sip = *(uint32_t*)sip; addr.m_fields.m_sport = sinfo->m_ipv4info.m_fields.m_sport; addr.m_fields.m_dip = *(uint32_t*)dip; addr.m_fields.m_dport = sinfo->m_ipv4info.m_fields.m_dport; addr.m_fields.m_l4proto = sinfo->m_ipv4info.m_fields.m_l4proto; string straddr = ipv4tuple_to_string(&addr, resolve); snprintf(targetbuf, targetbuf_size, "%s", straddr.c_str()); return true; } else { char srcstr[INET6_ADDRSTRLEN]; char dststr[INET6_ADDRSTRLEN]; if(inet_ntop(AF_INET6, sip6, srcstr, sizeof(srcstr)) && inet_ntop(AF_INET6, sip6, dststr, sizeof(dststr))) { snprintf(targetbuf, targetbuf_size, "%s:%s->%s:%s", srcstr, port_to_string(sinfo->m_ipv6info.m_fields.m_sport, sinfo->m_ipv6info.m_fields.m_l4proto, resolve).c_str(), dststr, port_to_string(sinfo->m_ipv6info.m_fields.m_dport, sinfo->m_ipv6info.m_fields.m_l4proto, resolve).c_str()); return true; } } } else if(sinfo->m_ipv6info.m_fields.m_l4proto == SCAP_L4_ICMP) { if(sinsp_utils::is_ipv4_mapped_ipv6(sip6) && sinsp_utils::is_ipv4_mapped_ipv6(dip6)) { snprintf(targetbuf, targetbuf_size, "%u.%u.%u.%u->%u.%u.%u.%u", (unsigned int)sip[0], (unsigned int)sip[1], (unsigned int)sip[2], (unsigned int)sip[3], (unsigned int)dip[0], (unsigned int)dip[1], (unsigned int)dip[2], (unsigned int)dip[3]); return true; } else { char srcstr[INET6_ADDRSTRLEN]; char dststr[INET6_ADDRSTRLEN]; if(inet_ntop(AF_INET6, sip6, srcstr, sizeof(srcstr)) && inet_ntop(AF_INET6, sip6, dststr, sizeof(dststr))) { snprintf(targetbuf, targetbuf_size, "%s->%s", srcstr, dststr); return true; } } } else { snprintf(targetbuf, targetbuf_size, ""); } } return true; } // // Helper function to move a directory up in a path string // void rewind_to_parent_path(char* targetbase, char** tc, const char** pc, uint32_t delta) { if(*tc <= targetbase + 1) { (*pc) += delta; return; } (*tc)--; while(*((*tc) - 1) != '/' && (*tc) >= targetbase + 1) { (*tc)--; } (*pc) += delta; } // // Args: // - target: the string where we are supposed to start copying // - targetbase: the base of the path, i.e. the furthest we can go back when // following parent directories // - path: the path to copy // void copy_and_sanitize_path(char* target, char* targetbase, const char* path) { char* tc = target; const char* pc = path; g_invalidchar ic; while(true) { if(*pc == 0) { *tc = 0; // // If the path ends with a '/', remove it, as the OS does. // if((tc > (targetbase + 1)) && (*(tc - 1) == '/')) { *(tc - 1) = 0; } return; } if(ic(*pc)) { // // Invalid char, substitute with a '.' // *tc = '.'; tc++; pc++; } else { if(*pc == '.' && *(pc + 1) == '.' && *(pc + 2) == '/') { // // '../', rewind to the previous '/' // rewind_to_parent_path(targetbase, &tc, &pc, 3); } else if(*pc == '.' && *(pc + 1) == '.') { // // '..', with no '/'. // This is valid if we are at the end of the string, and in that case we rewind. // Otherwise it shouldn't happen and we leave the string intact // if(*(pc + 2) == 0) { rewind_to_parent_path(targetbase, &tc, &pc, 2); } else { *tc = '.'; *(tc + 1) = '.'; pc += 2; tc += 2; } } else if(*pc == '.' && *(pc + 1) == '/') { // // './', just skip it // pc += 2; } else if(*pc == '.') { // // '.', with no '/'. // This is valid if we are at the end of the string, and in that case we rewind. // Otherwise it shouldn't happen and we leave the string intact // if(*(pc + 1) == 0) { pc++; } else { *tc = *pc; tc++; pc++; } } else if(*pc == '/') { // // '/', if the last char is already a '/', skip it // if(tc > targetbase && *(tc - 1) == '/') { pc++; } else { *tc = *pc; tc++; pc++; } } else { // // Normal char, copy it // *tc = *pc; tc++; pc++; } } } } // // Return false if path2 is an absolute path // bool sinsp_utils::concatenate_paths(char* target, uint32_t targetlen, const char* path1, uint32_t len1, const char* path2, uint32_t len2) { if(targetlen < (len1 + len2 + 1)) { strcpy(target, "/PATH_TOO_LONG"); return false; } if(len2 != 0 && path2[0] != '/') { memcpy(target, path1, len1); copy_and_sanitize_path(target + len1, target, path2); return true; } else { target[0] = 0; copy_and_sanitize_path(target, target, path2); return false; } } bool sinsp_utils::is_ipv4_mapped_ipv6(uint8_t* paddr) { if(paddr[0] == 0 && paddr[1] == 0 && paddr[2] == 0 && paddr[3] == 0 && paddr[4] == 0 && paddr[5] == 0 && paddr[6] == 0 && paddr[7] == 0 && paddr[8] == 0 && paddr[9] == 0 && ( ( paddr[10] == 0xff && paddr[11] == 0xff) || // A real IPv4 address (paddr[10] == 0 && paddr[11] == 0 && paddr[12] == 0 && paddr[13] == 0 && paddr[14] == 0 && paddr[15] == 0) // all zero address, assume IPv4 as well ) ) { return true; } else { return false; } } const struct ppm_param_info* sinsp_utils::find_longest_matching_evt_param(string name) { uint32_t maxlen = 0; const struct ppm_param_info* res = NULL; for(uint32_t j = 0; j < PPM_EVENT_MAX; j++) { const ppm_event_info* ei = &g_infotables.m_event_info[j]; for(uint32_t k = 0; k < ei->nparams; k++) { const struct ppm_param_info* pi = &ei->params[k]; const char* an = pi->name; uint32_t alen = (uint32_t)strlen(an); string subs = string(name, 0, alen); if(subs == an) { if(alen > maxlen) { res = pi; maxlen = alen; } } } } return res; } #ifdef HAS_FILTERING void sinsp_utils::get_filtercheck_fields_info(OUT vector* list) { g_filterlist.get_all_fields(list); } #endif uint64_t sinsp_utils::get_current_time_ns() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * (uint64_t) 1000000000 + tv.tv_usec * 1000; } bool sinsp_utils::glob_match(const char *pattern, const char *string) { #ifdef _WIN32 return PathMatchSpec(string, pattern) == TRUE; #else int flags = 0; return fnmatch(pattern, string, flags) == 0; #endif } #ifndef CYGWING_AGENT #ifndef _WIN32 void sinsp_utils::bt(void) { static const char start[] = "BACKTRACE ------------"; static const char end[] = "----------------------"; void *bt[1024]; int bt_size; char **bt_syms; int i; bt_size = backtrace(bt, 1024); bt_syms = backtrace_symbols(bt, bt_size); g_logger.format("%s", start); for (i = 1; i < bt_size; i++) { g_logger.format("%s", bt_syms[i]); } g_logger.format("%s", end); free(bt_syms); } #endif // _WIN32 #endif // CYGWING_AGENT bool sinsp_utils::find_first_env(std::string &out, const vector &env, const vector &keys) { for (const string key : keys) { for(const auto& env_var : env) { if((env_var.size() > key.size()) && !env_var.compare(0, key.size(), key) && (env_var[key.size()] == '=')) { out = env_var.substr(key.size()+1); return true; } } } return false; } bool sinsp_utils::find_env(std::string &out, const vector &env, const std::string &key) { const vector keys = { key }; return find_first_env(out, env, keys); } void sinsp_utils::split_container_image(const std::string &image, std::string &hostname, std::string &port, std::string &name, std::string &tag, std::string &digest, bool split_repo) { auto split = [](const std::string &src, std::string &part1, std::string &part2, const std::string sep) { size_t pos = src.find(sep); if(pos != std::string::npos) { part1 = src.substr(0, pos); part2 = src.substr(pos+1); return true; } return false; }; std::string hostport, rem, rem2, repo; hostname = port = name = tag = digest = ""; if(split(image, hostport, rem, "/")) { repo = hostport + "/"; if(!split(hostport, hostname, port, ":")) { hostname = hostport; port = ""; } } else { hostname = ""; port = ""; rem = image; } if(split(rem, rem2, digest, "@")) { if(!split(rem2, name, tag, ":")) { name = rem2; tag = ""; } } else { digest = ""; if(!split(rem, name, tag, ":")) { name = rem; tag = ""; } } if(!split_repo) { name = repo + name; } } void sinsp_utils::parse_suppressed_types(const std::vector &supp_strs, std::vector *supp_ids) { for (auto ii = 0; ii < PPM_EVENT_MAX; ii++) { auto iter = std::find(supp_strs.begin(), supp_strs.end(), event_name_by_id(ii)); if (iter != supp_strs.end()) { supp_ids->push_back(ii); } } } const char* sinsp_utils::event_name_by_id(uint16_t id) { if (id >= PPM_EVENT_MAX) { ASSERT(false); return "NA"; } return g_infotables.m_event_info[id].name; } void sinsp_utils::ts_to_string(uint64_t ts, OUT string* res, bool date, bool ns) { struct tm *tm; time_t Time; uint64_t sec = ts / ONE_SECOND_IN_NS; uint64_t nsec = ts % ONE_SECOND_IN_NS; int32_t thiszone = gmt2local(0); int32_t s = (sec + thiszone) % 86400; int32_t bufsize = 0; char buf[256]; if(date) { Time = (sec + thiszone) - s; tm = gmtime (&Time); if(!tm) { bufsize = sprintf(buf, " "); } else { bufsize = sprintf(buf, "%04d-%02d-%02d ", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday); } } if(ns) { sprintf(buf + bufsize, "%02d:%02d:%02d.%09u", s / 3600, (s % 3600) / 60, s % 60, (unsigned)nsec); } else { sprintf(buf + bufsize, "%02d:%02d:%02d", s / 3600, (s % 3600) / 60, s % 60); } *res = buf; } void sinsp_utils::ts_to_iso_8601(uint64_t ts, OUT string* res) { static const char *fmt = "YYYY-MM-DDTHH:MM:SS-0000"; static const uint32_t fmtlen = strlen(fmt); char buf[fmtlen]; uint64_t ns = ts % ONE_SECOND_IN_NS; time_t sec = ts / ONE_SECOND_IN_NS; if(strftime(buf, sizeof(buf), "%FT%T", gmtime(&sec)) == 0) { *res = fmt; return; } *res = buf; if(sprintf(buf, ".%09u", (unsigned) ns) < 0) { *res = fmt; return; } *res += buf; if(strftime(buf, sizeof(buf), "%z", gmtime(&sec)) == 0) { *res = fmt; return; } *res += buf; } /////////////////////////////////////////////////////////////////////////////// // Time utility functions. /////////////////////////////////////////////////////////////////////////////// bool sinsp_utils::parse_iso_8601_utc_string(const std::string& time_str, uint64_t &ns) { #ifndef _WIN32 char *rem; struct tm tm_time = {0}; rem = strptime(time_str.c_str(), "%Y-%m-%dT%H:%M:", &tm_time); if(rem == NULL || *rem == '\0') { return false; } tm_time.tm_isdst = -1; // strptime does not set this, signal timegm to determine DST ns = timegm(&tm_time) * ONE_SECOND_IN_NS; // Handle the possibly fractional seconds now. Also verify // that the string ends with Z. double fractional_secs; if(sscanf(rem, "%lfZ", &fractional_secs) != 1) { return false; } ns += (fractional_secs * ONE_SECOND_IN_NS); return true; #else throw sinsp_exception("parse_iso_8601_utc_string() not implemented on Windows"); #endif } time_t get_epoch_utc_seconds(const std::string& time_str, const std::string& fmt) { #ifndef _WIN32 if(time_str.empty() || fmt.empty()) { throw sinsp_exception("get_epoch_utc_seconds(): empty time or format string."); } struct tm tm_time = {0}; strptime(time_str.c_str(), fmt.c_str(), &tm_time); tm_time.tm_isdst = -1; // strptime does not set this, signal timegm to determine DST return timegm(&tm_time); #else throw sinsp_exception("get_epoch_utc_seconds() not implemented on Windows"); #endif // _WIN32 } time_t get_epoch_utc_seconds_now() { #ifndef _WIN32 time_t rawtime; time(&rawtime); return timegm(gmtime(&rawtime)); #else throw sinsp_exception("get_now_seconds() not implemented on Windows"); #endif // _WIN32 } // gettimeofday() windows implementation #ifdef _WIN32 #include #include const __int64 DELTA_EPOCH_IN_MICROSECS = 11644473600000000; int gettimeofday(struct timeval *tv, struct timezone2 *tz) { FILETIME ft; __int64 tmpres = 0; TIME_ZONE_INFORMATION tz_winapi; int rez=0; ZeroMemory(&ft,sizeof(ft)); ZeroMemory(&tz_winapi,sizeof(tz_winapi)); GetSystemTimeAsFileTime(&ft); tmpres = ft.dwHighDateTime; tmpres <<= 32; tmpres |= ft.dwLowDateTime; // // converting file time to unix epoch // tmpres /= 10; // convert into microseconds tmpres -= DELTA_EPOCH_IN_MICROSECS; tv->tv_sec = (__int32)(tmpres*0.000001); tv->tv_usec =(tmpres%1000000); // // _tzset(),don't work properly, so we use GetTimeZoneInformation // if(tz) { rez=GetTimeZoneInformation(&tz_winapi); tz->tz_dsttime=(rez==2)?true:false; tz->tz_minuteswest = tz_winapi.Bias + ((rez==2)?tz_winapi.DaylightBias:0); } return 0; } #endif // _WIN32 /////////////////////////////////////////////////////////////////////////////// // gethostname wrapper /////////////////////////////////////////////////////////////////////////////// string sinsp_gethostname() { char hname[256]; int res = gethostname(hname, sizeof(hname) / sizeof(hname[0])); if(res == 0) { return hname; } else { ASSERT(false); return ""; } } /////////////////////////////////////////////////////////////////////////////// // tuples to string /////////////////////////////////////////////////////////////////////////////// string port_to_string(uint16_t port, uint8_t l4proto, bool resolve) { string ret = ""; if(resolve) { string proto = ""; if(l4proto == SCAP_L4_TCP) { proto = "tcp"; } else if(l4proto == SCAP_L4_UDP) { proto = "udp"; } // `port` is saved with network byte order struct servent * res; res = getservbyport(ntohs(port), (proto != "") ? proto.c_str() : NULL); // best effort! if (res) { ret = res->s_name; } else { ret = to_string(port); } } else { ret = to_string(port); } return ret; } string ipv4serveraddr_to_string(ipv4serverinfo* addr, bool resolve) { char buf[50]; uint8_t *ip = (uint8_t *)&addr->m_ip; // IP address is in network byte order regardless of host endianness snprintf(buf, sizeof(buf), "%d.%d.%d.%d:%s", ip[0], ip[1], ip[2], ip[3], port_to_string(addr->m_port, addr->m_l4proto, resolve).c_str()); return string(buf); } string ipv4tuple_to_string(ipv4tuple* tuple, bool resolve) { char buf[100]; ipv4serverinfo info; info.m_ip = tuple->m_fields.m_sip; info.m_port = tuple->m_fields.m_sport; info.m_l4proto = tuple->m_fields.m_l4proto; string source = ipv4serveraddr_to_string(&info, resolve); info.m_ip = tuple->m_fields.m_dip; info.m_port = tuple->m_fields.m_dport; info.m_l4proto = tuple->m_fields.m_l4proto; string dest = ipv4serveraddr_to_string(&info, resolve); snprintf(buf, sizeof(buf), "%s->%s", source.c_str(), dest.c_str()); return string(buf); } string ipv6serveraddr_to_string(ipv6serverinfo* addr, bool resolve) { char address[100]; char buf[200]; if(NULL == inet_ntop(AF_INET6, addr->m_ip.m_b, address, 100)) { return string(); } snprintf(buf,200,"%s:%s", address, port_to_string(addr->m_port, addr->m_l4proto, resolve).c_str()); return string(buf); } string ipv6tuple_to_string(_ipv6tuple* tuple, bool resolve) { char source_address[100]; char destination_address[100]; char buf[200]; if(NULL == inet_ntop(AF_INET6, tuple->m_fields.m_sip.m_b, source_address, 100)) { return string(); } if(NULL == inet_ntop(AF_INET6, tuple->m_fields.m_dip.m_b, destination_address, 100)) { return string(); } snprintf(buf,200,"%s:%s->%s:%s", source_address, port_to_string(tuple->m_fields.m_sport, tuple->m_fields.m_l4proto, resolve).c_str(), destination_address, port_to_string(tuple->m_fields.m_dport, tuple->m_fields.m_l4proto, resolve).c_str()); return string(buf); } const char* param_type_to_string(ppm_param_type pt) { switch(pt) { case PT_NONE: return "NONE"; case PT_INT8: return "INT8"; case PT_INT16: return "INT16"; case PT_INT32: return "INT32"; case PT_INT64: return "INT64"; case PT_UINT8: return "UINT8"; case PT_UINT16: return "UINT16"; case PT_UINT32: return "UINT32"; case PT_UINT64: return "UINT64"; case PT_CHARBUF: return "CHARBUF"; case PT_BYTEBUF: return "BYTEBUF"; case PT_ERRNO: return "ERRNO"; case PT_SOCKADDR: return "SOCKADDR"; case PT_SOCKTUPLE: return "SOCKTUPLE"; case PT_FD: return "FD"; case PT_PID: return "PID"; case PT_FDLIST: return "FDLIST"; case PT_FSPATH: return "FSPATH"; case PT_SYSCALLID: return "SYSCALLID"; case PT_SIGTYPE: return "SIGTYPE"; case PT_RELTIME: return "RELTIME"; case PT_ABSTIME: return "ABSTIME"; case PT_PORT: return "PORT"; case PT_L4PROTO: return "L4PROTO"; case PT_SOCKFAMILY: return "SOCKFAMILY"; case PT_BOOL: return "BOOL"; case PT_IPV4ADDR: return "IPV4ADDR"; case PT_DYN: return "DYNAMIC"; case PT_FLAGS8: return "FLAGS8"; case PT_FLAGS16: return "FLAGS16"; case PT_FLAGS32: return "FLAGS32"; case PT_MODE: return "MODE"; case PT_UID: return "UID"; case PT_GID: return "GID"; case PT_SIGSET: return "SIGSET"; case PT_IPV4NET: return "IPV4NET"; case PT_DOUBLE: return "DOUBLE"; case PT_CHARBUFARRAY: return "CHARBUFARRAY"; case PT_CHARBUF_PAIR_ARRAY: return "CHARBUF_PAIR_ARRAY"; default: ASSERT(false); return ""; } } const char* print_format_to_string(ppm_print_format fmt) { switch(fmt) { case PF_DEC: return "DEC"; case PF_HEX: return "HEX"; case PF_10_PADDED_DEC: return "10_PADDED_DEC"; case PF_ID: return "ID"; case PF_DIR: return "DIR"; case PF_OCT: return "OCT"; case PF_NA: return "NA"; default: ASSERT(false); return "NA"; } } /////////////////////////////////////////////////////////////////////////////// // String helpers /////////////////////////////////////////////////////////////////////////////// // // String split // vector sinsp_split(const string &s, char delim) { vector res; istringstream f(s); string ts; while(getline(f, ts, delim)) { res.push_back(ts); } return res; } // // trim from start // string& ltrim(string &s) { s.erase(s.begin(), find_if(s.begin(), s.end(), not1(ptr_fun(isspace)))); return s; } // // trim from end // string& rtrim(string &s) { s.erase(find_if(s.rbegin(), s.rend(), not1(ptr_fun(isspace))).base(), s.end()); return s; } // // trim from both ends // string& trim(string &s) { return ltrim(rtrim(s)); } string& replace_in_place(string& str, const string& search, const string& replacement) { string::size_type ssz = search.length(); string::size_type rsz = replacement.length(); string::size_type pos = 0; while((pos = str.find(search, pos)) != string::npos) { str.replace(pos, ssz, replacement); pos += rsz; ASSERT(pos <= str.length()); } return str; } string replace(const string& str, const string& search, const string& replacement) { string s(str); replace_in_place(s, search, replacement); return s; } bool sinsp_utils::endswith(const string& str, const string& ending) { if (ending.size() <= str.size()) { return (0 == str.compare(str.length() - ending.length(), ending.length(), ending)); } return false; } bool sinsp_utils::endswith(const char *str, const char *ending, uint32_t lstr, uint32_t lend) { if (lstr >= lend) { return (0 == memcmp(ending, str + (lstr - lend), lend)); } return 0; } /////////////////////////////////////////////////////////////////////////////// // sinsp_numparser implementation /////////////////////////////////////////////////////////////////////////////// uint8_t sinsp_numparser::parseu8(const string& str) { uint32_t res; char temp; if(std::sscanf(str.c_str(), "%" PRIu32 "%c", &res, &temp) != 1) { throw sinsp_exception(str + " is not a valid number"); } return (uint8_t)res; } int8_t sinsp_numparser::parsed8(const string& str) { int32_t res; char temp; if(std::sscanf(str.c_str(), "%" PRId32 "%c", &res, &temp) != 1) { throw sinsp_exception(str + " is not a valid number"); } return (int8_t)res; } uint16_t sinsp_numparser::parseu16(const string& str) { uint32_t res; char temp; if(std::sscanf(str.c_str(), "%" PRIu32 "%c", &res, &temp) != 1) { throw sinsp_exception(str + " is not a valid number"); } return (uint16_t)res; } int16_t sinsp_numparser::parsed16(const string& str) { int32_t res; char temp; if(std::sscanf(str.c_str(), "%" PRId32 "%c", &res, &temp) != 1) { throw sinsp_exception(str + " is not a valid number"); } return (int16_t)res; } uint32_t sinsp_numparser::parseu32(const string& str) { uint32_t res; char temp; if(std::sscanf(str.c_str(), "%" PRIu32 "%c", &res, &temp) != 1) { throw sinsp_exception(str + " is not a valid number"); } return res; } int32_t sinsp_numparser::parsed32(const string& str) { int32_t res; char temp; if(std::sscanf(str.c_str(), "%" PRId32 "%c", &res, &temp) != 1) { throw sinsp_exception(str + " is not a valid number"); } return res; } uint64_t sinsp_numparser::parseu64(const string& str) { uint64_t res; char temp; if(std::sscanf(str.c_str(), "%" PRIu64 "%c", &res, &temp) != 1) { throw sinsp_exception(str + " is not a valid number"); } return res; } int64_t sinsp_numparser::parsed64(const string& str) { int64_t res; char temp; if(std::sscanf(str.c_str(), "%" PRId64 "%c", &res, &temp) != 1) { throw sinsp_exception(str + " is not a valid number"); } return res; } bool sinsp_numparser::tryparseu32(const string& str, uint32_t* res) { char temp; if(std::sscanf(str.c_str(), "%" PRIu32 "%c", res, &temp) != 1) { return false; } return true; } bool sinsp_numparser::tryparsed32(const string& str, int32_t* res) { char temp; if(std::sscanf(str.c_str(), "%" PRId32 "%c", res, &temp) != 1) { return false; } return true; } bool sinsp_numparser::tryparseu64(const string& str, uint64_t* res) { char temp; if(std::sscanf(str.c_str(), "%" PRIu64 "%c", res, &temp) != 1) { return false; } return true; } bool sinsp_numparser::tryparsed64(const string& str, int64_t* res) { char temp; if(std::sscanf(str.c_str(), "%" PRId64 "%c", res, &temp) != 1) { return false; } return true; } bool sinsp_numparser::tryparseu32_fast(const char* str, uint32_t strlen, uint32_t* res) { const char* p = str; const char* end = str + strlen; *res = 0; while(p < end) { if(*p >= '0' && *p <= '9') { *res = (*res) * 10 + (*p - '0'); } else { return false; } p++; } return true; } bool sinsp_numparser::tryparsed32_fast(const char* str, uint32_t strlen, int32_t* res) { const char* p = str; const char* end = str + strlen; *res = 0; while(p < end) { if(*p >= '0' && *p <= '9') { *res = (*res) * 10 + (*p - '0'); } else { return false; } p++; } return true; } /////////////////////////////////////////////////////////////////////////////// // JSON helpers /////////////////////////////////////////////////////////////////////////////// std::string get_json_string(const Json::Value& obj, const std::string& name) { std::string ret; const Json::Value& json_val = obj[name]; if(!json_val.isNull() && json_val.isConvertibleTo(Json::stringValue)) { ret = json_val.asString(); } return ret; } /////////////////////////////////////////////////////////////////////////////// // socket helpers /////////////////////////////////////////////////////////////////////////////// bool set_socket_blocking(int sock, bool block) { #ifndef _WIN32 int arg = block ? 0 : 1; if(ioctl(sock, FIONBIO, &arg) == -1) #else u_long arg = block ? 0 : 1; if(ioctlsocket(sock, FIONBIO, &arg) == -1) #endif // _WIN32 { return false; } return true; } unsigned int read_num_possible_cpus(void) { static const char *fcpu = "/sys/devices/system/cpu/possible"; unsigned int start, end, possible_cpus = 0; char buff[128]; FILE *fp; fp = fopen(fcpu, "r"); if (!fp) { return possible_cpus; } while (fgets(buff, sizeof(buff), fp)) { if (sscanf(buff, "%u-%u", &start, &end) == 2) { possible_cpus = start == 0 ? end + 1 : 0; break; } } fclose(fp); return possible_cpus; } sysdig-0.26.4/userspace/libsinsp/utils.h000066400000000000000000000273441352731327100202510ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #include #include #include #include #include #include #include #include #include "json/json.h" class sinsp_evttables; typedef union _sinsp_sockinfo sinsp_sockinfo; class filter_check_info; extern sinsp_evttables g_infotables; /////////////////////////////////////////////////////////////////////////////// // 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 sinsp_initializer { public: sinsp_initializer(); ~sinsp_initializer(); }; /////////////////////////////////////////////////////////////////////////////// // A collection of useful functions /////////////////////////////////////////////////////////////////////////////// class sinsp_utils { public: // // Convert an errno number into the corresponding compact code // static const char* errno_to_str(int32_t code); // // Convert a signal number into the corresponding signal name // static const char* signal_to_str(uint8_t code); // // // static bool sockinfo_to_str(sinsp_sockinfo* sinfo, scap_fd_type stype, char* targetbuf, uint32_t targetbuf_size, bool resolve = false); // // Check if string ends with another // static bool endswith(const std::string& str, const std::string& ending); static bool endswith(const char *str, const char *ending, uint32_t lstr, uint32_t lend); // // Concatenate two paths and puts the result in "target". // If path2 is relative, the concatenation happens and the result is true. // If path2 is absolute, the concatenation does not happen, target contains path2 and the result is false. // Assumes that path1 is well formed. // static bool concatenate_paths(char* target, uint32_t targetlen, const char* path1, uint32_t len1, const char* path2, uint32_t len2); // // Determines if an IPv6 address is IPv4-mapped // static bool is_ipv4_mapped_ipv6(uint8_t* paddr); // // Given a string, scan the event list and find the longest argument that the input string contains // static const struct ppm_param_info* find_longest_matching_evt_param(std::string name); // // Get the list of filtercheck fields // static void get_filtercheck_fields_info(std::vector* list); static uint64_t get_current_time_ns(); static bool glob_match(const char *pattern, const char *string); #ifndef _WIN32 // // Print the call stack // static void bt(void); #endif // _WIN32 static bool find_first_env(std::string &out, const std::vector &env, const std::vector &keys); static bool find_env(std::string &out, const std::vector &env, const std::string &key); static void split_container_image(const std::string &image, std::string &hostname, std::string &port, std::string &name, std::string &tag, std::string &digest, bool split_repo = true); static void parse_suppressed_types(const std::vector &supp_strs, std::vector *supp_ids); static const char* event_name_by_id(uint16_t id); static void ts_to_string(uint64_t ts, OUT std::string* res, bool date, bool ns); static void ts_to_iso_8601(uint64_t ts, OUT std::string* res); // Limited version of iso 8601 time string parsing, that assumes a // timezone of Z for UTC, but does support parsing fractional seconds, // unlike get_epoch_utc_seconds_* below. static bool parse_iso_8601_utc_string(const std::string& time_str, uint64_t &ns); }; /////////////////////////////////////////////////////////////////////////////// // little STL thing to sanitize strings /////////////////////////////////////////////////////////////////////////////// struct g_invalidchar { bool operator()(char c) const { if(c < -1) { return true; } return !isprint((unsigned)c); } }; inline void sanitize_string(std::string &str) { // It turns out with -O3 (release flags) using erase and // remove_if is slightly faster than the inline version that // was here. It's not faster for -O2, and is actually much // slower without optimization. // // Optimize for the release case, then. str.erase(remove_if(str.begin(), str.end(), g_invalidchar()), str.end()); } /////////////////////////////////////////////////////////////////////////////// // Time utility functions. /////////////////////////////////////////////////////////////////////////////// time_t get_epoch_utc_seconds(const std::string& time_str, const std::string& fmt = "%Y-%m-%dT%H:%M:%SZ"); time_t get_epoch_utc_seconds_now(); // Time functions for Windows #ifdef _WIN32 struct timezone2 { int32_t tz_minuteswest; bool tz_dsttime; }; SINSP_PUBLIC int gettimeofday(struct timeval *tv, struct timezone2 *tz); #endif // _WIN32 /////////////////////////////////////////////////////////////////////////////// // gethostname wrapper /////////////////////////////////////////////////////////////////////////////// std::string sinsp_gethostname(); /////////////////////////////////////////////////////////////////////////////// // tuples to string /////////////////////////////////////////////////////////////////////////////// // each of these functions uses values in network byte order std::string ipv4tuple_to_string(ipv4tuple* tuple, bool resolve); std::string ipv6tuple_to_string(ipv6tuple* tuple, bool resolve); std::string ipv4serveraddr_to_string(ipv4serverinfo* addr, bool resolve); std::string ipv6serveraddr_to_string(ipv6serverinfo* addr, bool resolve); // `l4proto` should be of type scap_l4_proto, but since it's an enum sometimes // is used as int and we would have to cast // `port` must be saved with network byte order // `l4proto` could be neither TCP nor UDP, in this case any protocol will be // matched std::string port_to_string(uint16_t port, uint8_t l4proto, bool resolve); const char* param_type_to_string(ppm_param_type pt); const char* print_format_to_string(ppm_print_format fmt); /////////////////////////////////////////////////////////////////////////////// // String helpers /////////////////////////////////////////////////////////////////////////////// std::vector sinsp_split(const std::string& s, char delim); template std::string sinsp_join(It begin, It end, char delim) { if(begin == end) { return ""; } std::stringstream ss; ss << *begin; ++begin; for(auto it = begin; it != end; ++it) { ss << delim << *it; } return ss.str(); } std::string& ltrim(std::string& s); std::string& rtrim(std::string& s); std::string& trim(std::string& s); std::string& replace_in_place(std::string& s, const std::string& search, const std::string& replacement); std::string replace(const std::string& str, const std::string& search, const std::string& replacement); /////////////////////////////////////////////////////////////////////////////// // number parser /////////////////////////////////////////////////////////////////////////////// class sinsp_numparser { public: static uint8_t parseu8(const std::string& str); static int8_t parsed8(const std::string& str); static uint16_t parseu16(const std::string& str); static int16_t parsed16(const std::string& str); static uint32_t parseu32(const std::string& str); static int32_t parsed32(const std::string& str); static uint64_t parseu64(const std::string& str); static int64_t parsed64(const std::string& str); static bool tryparseu32(const std::string& str, uint32_t* res); static bool tryparsed32(const std::string& str, int32_t* res); static bool tryparseu64(const std::string& str, uint64_t* res); static bool tryparsed64(const std::string& str, int64_t* res); static bool tryparseu32_fast(const char* str, uint32_t strlen, uint32_t* res); static bool tryparsed32_fast(const char* str, uint32_t strlen, int32_t* res); }; /////////////////////////////////////////////////////////////////////////////// // JSON helpers /////////////////////////////////////////////////////////////////////////////// namespace Json { class Value; } std::string get_json_string(const Json::Value& obj, const std::string& name); inline std::string json_as_string(const Json::Value& json) { return Json::FastWriter().write(json); } /////////////////////////////////////////////////////////////////////////////// // A simple class to manage pre-allocated objects in a LIFO // fashion and make sure all of them are deleted upon destruction. /////////////////////////////////////////////////////////////////////////////// template class simple_lifo_queue { public: simple_lifo_queue(uint32_t size) { uint32_t j; for(j = 0; j < size; j++) { OBJ* newentry = new OBJ; m_full_list.push_back(newentry); m_avail_list.push_back(newentry); } } ~simple_lifo_queue() { while(!m_avail_list.empty()) { OBJ* head = m_avail_list.front(); delete head; m_avail_list.pop_front(); } } void push(OBJ* newentry) { m_avail_list.push_front(newentry); } OBJ* pop() { if(m_avail_list.empty()) { return NULL; } OBJ* head = m_avail_list.front(); m_avail_list.pop_front(); return head; } bool empty() { return m_avail_list.empty(); } private: std::list m_avail_list; std::list m_full_list; }; /////////////////////////////////////////////////////////////////////////////// // Case-insensitive string find. /////////////////////////////////////////////////////////////////////////////// template struct ci_equal { ci_equal(const std::locale& loc) : m_loc(loc) {} bool operator()(charT ch1, charT ch2) { return std::toupper(ch1, m_loc) == std::toupper(ch2, m_loc); } private: const std::locale& m_loc; }; template int ci_find_substr(const T& str1, const T& str2, const std::locale& loc = std::locale()) { typename T::const_iterator it = std::search(str1.begin(), str1.end(), str2.begin(), str2.end(), ci_equal(loc) ); if(it != str1.end()) { return it - str1.begin(); } return -1; } struct ci_compare { // less-than, for use in STL containers bool operator() (const std::string& a, const std::string& b) const { #ifndef _WIN32 return strcasecmp(a.c_str(), b.c_str()) < 0; #else return lstrcmpiA(a.c_str(), b.c_str()) < 0; #endif // _WIN32 } static bool is_equal(const std::string& a, const std::string& b) { #ifndef _WIN32 return strcasecmp(a.c_str(), b.c_str()) == 0; #else return lstrcmpiA(a.c_str(), b.c_str()) == 0; #endif // _WIN32 } }; /////////////////////////////////////////////////////////////////////////////// // socket helpers /////////////////////////////////////////////////////////////////////////////// bool set_socket_blocking(int sock, bool block); unsigned int read_num_possible_cpus(void); /////////////////////////////////////////////////////////////////////////////// // hashing helpers /////////////////////////////////////////////////////////////////////////////// // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf template inline void hash_combine(std::size_t &seed, const T& val) { seed ^= std::hash()(val) + 0x9e3779b9 + (seed<<6) + (seed>>2); } sysdig-0.26.4/userspace/libsinsp/value_parser.cpp000066400000000000000000000121641352731327100221260ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "sinsp.h" #include "sinsp_int.h" #include "value_parser.h" #ifdef _WIN32 #pragma comment(lib, "Ws2_32.lib") #include #else #include #endif size_t sinsp_filter_value_parser::string_to_rawval(const char* str, uint32_t len, uint8_t *storage, string::size_type max_len, ppm_param_type ptype) { size_t parsed_len; switch(ptype) { case PT_INT8: *(int8_t*)storage = sinsp_numparser::parsed8(str); parsed_len = sizeof(int8_t); break; case PT_INT16: *(int16_t*)storage = sinsp_numparser::parsed16(str); parsed_len = sizeof(int16_t); break; case PT_INT32: *(int32_t*)storage = sinsp_numparser::parsed32(str); parsed_len = sizeof(int32_t); break; case PT_INT64: case PT_FD: case PT_ERRNO: *(int64_t*)storage = sinsp_numparser::parsed64(str); parsed_len = sizeof(int64_t); break; case PT_L4PROTO: // This can be resolved in the future case PT_FLAGS8: case PT_UINT8: *(uint8_t*)storage = sinsp_numparser::parseu8(str); parsed_len = sizeof(int8_t); break; case PT_PORT: { string in(str); if(in.empty()) { *(uint16_t*)storage = 0; } else { // if the string is made only of numbers if(strspn(in.c_str(), "0123456789") == in.size()) { *(uint16_t*)storage = stoi(in); } else { struct servent* se = getservbyname(in.c_str(), NULL); if(se == NULL) { throw sinsp_exception("unrecognized protocol " + in); } else { *(uint16_t*)storage = ntohs(getservbyname(in.c_str(), NULL)->s_port); } } } parsed_len = sizeof(int16_t); break; } case PT_FLAGS16: case PT_UINT16: *(uint16_t*)storage = sinsp_numparser::parseu16(str); parsed_len = sizeof(uint16_t); break; case PT_FLAGS32: case PT_UINT32: case PT_MODE: *(uint32_t*)storage = sinsp_numparser::parseu32(str); parsed_len = sizeof(uint32_t); break; case PT_UINT64: *(uint64_t*)storage = sinsp_numparser::parseu64(str); parsed_len = sizeof(uint64_t); break; case PT_RELTIME: case PT_ABSTIME: *(uint64_t*)storage = sinsp_numparser::parseu64(str); parsed_len = sizeof(uint64_t); break; case PT_CHARBUF: case PT_SOCKADDR: case PT_SOCKFAMILY: { len = (uint32_t)strlen(str); if(len >= max_len) { throw sinsp_exception("filter parameter too long:" + string(str)); } memcpy(storage, str, len); *(uint8_t*)(&storage[len]) = 0; parsed_len = len; } break; case PT_BOOL: parsed_len = sizeof(uint32_t); if(string(str) == "true") { *(uint32_t*)storage = 1; } else if(string(str) == "false") { *(uint32_t*)storage = 0; } else { throw sinsp_exception("filter error: unrecognized boolean value " + string(str)); } break; case PT_IPADDR: if(memchr(str, '.', len) != NULL) { return string_to_rawval(str, len, storage, max_len, PT_IPV4ADDR); } else { return string_to_rawval(str, len, storage, max_len, PT_IPV6ADDR); } break; case PT_IPV4ADDR: if(inet_pton(AF_INET, str, storage) != 1) { throw sinsp_exception("unrecognized IPv4 address " + string(str)); } parsed_len = sizeof(struct in_addr); break; case PT_IPV6ADDR: case PT_IPV6NET: { ipv6addr *addr = (ipv6addr*) storage; if(inet_pton(AF_INET6, str, addr->m_b) != 1) { throw sinsp_exception("unrecognized IPv6 address " + string(str)); } parsed_len = sizeof(ipv6addr); break; } case PT_IPNET: if(memchr(str, '.', len) != NULL) { return string_to_rawval(str, len, storage, max_len, PT_IPV4NET); } else { return string_to_rawval(str, len, storage, max_len, PT_IPV6NET); } break; case PT_IPV4NET: { stringstream ss(str); string ip, mask; ipv4net* net = (ipv4net*)storage; if (strchr(str, '/') == NULL) { throw sinsp_exception("unrecognized IP network " + string(str)); } getline(ss, ip, '/'); getline(ss, mask); if(inet_pton(AF_INET, ip.c_str(), &net->m_ip) != 1) { throw sinsp_exception("unrecognized IP address " + string(str)); } uint32_t cidrlen = sinsp_numparser::parseu8(mask); if (cidrlen > 32) { throw sinsp_exception("invalid netmask " + mask); } uint32_t j; net->m_netmask = 0; for(j = 0; j < cidrlen; j++) { net->m_netmask |= 1<<(31-j); } net->m_netmask = htonl(net->m_netmask); parsed_len = sizeof(ipv4net); break; } default: ASSERT(false); throw sinsp_exception("wrong parameter type " + to_string((long long) ptype)); } return parsed_len; } sysdig-0.26.4/userspace/libsinsp/value_parser.h000066400000000000000000000017051352731327100215720ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once // // If this check is used by a filter, extract the constant to compare it to // Doesn't return the field length because the filtering engine can calculate it. // class sinsp_filter_value_parser { public: static size_t string_to_rawval(const char* str, uint32_t len, uint8_t *storage, string::size_type max_len, ppm_param_type ptype); }; sysdig-0.26.4/userspace/libsinsp/viewinfo.cpp000066400000000000000000000201571352731327100212650ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "sinsp.h" #include "sinsp_int.h" #include "sinsp_errno.h" #include "sinsp_signal.h" #include "filter.h" #include "filterchecks.h" #include "chisel.h" #include "protodecoder.h" /////////////////////////////////////////////////////////////////////////////// // sinsp_view_column_info implementation /////////////////////////////////////////////////////////////////////////////// string sinsp_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 sinsp_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); } } /////////////////////////////////////////////////////////////////////////////// // sinsp_view_info implementation /////////////////////////////////////////////////////////////////////////////// sinsp_view_info::sinsp_view_info() { m_valid = false; } sinsp_view_info::sinsp_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 sinsp_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((int64_t)m_sortingcol < 0) { ASSERT(false); throw sinsp_exception("view sorting column configuration error"); } } void sinsp_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 sinsp_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 sinsp_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) { sinsp_view_column_info ci = m_columns[j]; m_columns.erase(m_columns.begin() +j); m_columns.insert(m_columns.begin(), ci); return; } } } sinsp_view_column_info* sinsp_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 sinsp_view_info::get_filter(uint32_t depth) { 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; } } /////////////////////////////////////////////////////////////////////////////// // sinsp_view_manager implementation /////////////////////////////////////////////////////////////////////////////// void sinsp_view_manager::add(sinsp_view_info* vinfo) { m_views.push_back(*vinfo); } typedef struct view_cmp { bool operator()(const sinsp_view_info& src, const sinsp_view_info& dst) { return src.m_name < dst.m_name; } }table_row_cmp; void sinsp_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++) //{ // g_logger.format("> %d) %s", j, m_views[j].m_name.c_str()); //} } vector* sinsp_view_manager::get_views() { sort_views(); return &m_views; } uint32_t sinsp_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 sinsp_view_manager::set_selected_view(string viewid) { m_selected_view_id = viewid; } sysdig-0.26.4/userspace/libsinsp/viewinfo.h000066400000000000000000000116321352731327100207300ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #define VIEW_ID_SPY -1 #define VIEW_ID_DIG -2 #define VIEW_ID_INFO -3 // // Aggregation type for table fields // typedef enum sinsp_field_aggregation { A_NONE, A_SUM, A_AVG, A_TIME_AVG, A_MIN, A_MAX, }sinsp_field_aggregation; // // sinsp_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 sinsp_view_column_info { public: sinsp_view_column_info() { } sinsp_view_column_info(string field, string name, string description, uint32_t colsize, uint32_t flags, sinsp_field_aggregation aggregation, sinsp_field_aggregation groupby_aggregation, vector tags, 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; } string get_field(uint32_t depth); string get_filter_field(uint32_t depth); string m_field; string m_name; string m_description; uint32_t m_colsize; sinsp_field_aggregation m_aggregation; sinsp_field_aggregation m_groupby_aggregation; uint32_t m_flags; vector m_tags; string m_filterfield; }; /////////////////////////////////////////////////////////////////////////////// // action information /////////////////////////////////////////////////////////////////////////////// class sinsp_view_action_info { public: sinsp_view_action_info(char hotkey, string command, 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; string m_command; string m_description; bool m_ask_confirmation; bool m_waitfinish; }; /////////////////////////////////////////////////////////////////////////////// // View information /////////////////////////////////////////////////////////////////////////////// class sinsp_view_info { public: enum viewtype { T_NONE = 0, T_TABLE, T_LIST, T_TEXT, T_SPECTRO, }; sinsp_view_info(); sinsp_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); void get_col_names_and_sizes(OUT vector* colnames, OUT vector* colsizes); sinsp_view_column_info* get_key(); string get_filter(uint32_t depth); viewtype get_type() { return m_type; } bool does_groupby() { return m_does_groupby; } void apply_tag(string tag); void run_action(sinsp_view_action_info* action); string m_id; string m_name; string m_description; vector m_tags; vector m_tips; uint32_t m_sortingcol; vector m_applies_to; vector m_columns; bool m_use_defaults; bool m_does_groupby; viewtype m_type; bool m_valid; string m_drilldown_target; bool m_is_root; vector m_actions; vector m_col_sort_hotkeys; uint32_t max_col_sort_hotkeys; bool m_drilldown_increase_depth; bool m_propagate_filter; string m_spectro_type; 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 sinsp_view_manager { public: void add(sinsp_view_info* vinfo); vector* get_views(); uint32_t get_selected_view(); void set_selected_view(string viewid); size_t size() { return m_views.size(); } sinsp_view_info* at(uint32_t viewnum) { return &m_views[viewnum]; } private: void sort_views(); vector m_views; string m_selected_view_id; }; sysdig-0.26.4/userspace/sysdig.project000066400000000000000000010220441352731327100200000ustar00rootroot00000000000000 make -C ../build/debug all make -C ../build/debug clean make -C ../build/debug install None $(WorkspacePath) make -C ../build/release all make -C ../build/release clean make -C ../build/release install None $(WorkspacePath) sysdig-0.26.4/userspace/sysdig/000077500000000000000000000000001352731327100164055ustar00rootroot00000000000000sysdig-0.26.4/userspace/sysdig/CMakeLists.txt000066400000000000000000000060571352731327100211550ustar00rootroot00000000000000# # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # include_directories("${JSONCPP_INCLUDE}") include_directories("${TBB_INCLUDE_DIR}") if(NOT WIN32) include_directories("${CURL_INCLUDE_DIR}") include_directories("${CURSES_INCLUDE_DIR}") endif() include_directories("${PROJECT_SOURCE_DIR}/common") include_directories("${PROJECT_SOURCE_DIR}/userspace/libscap") include_directories("${PROJECT_SOURCE_DIR}/userspace/libsinsp") include_directories("${PROJECT_BINARY_DIR}/userspace/sysdig") include_directories(.) if(NOT WIN32) set(SOURCE_FILES fields_info.cpp sysdig.cpp) set(SOURCE_FILES_CSYSDIG fields_info.cpp csysdig.cpp) else() set(SOURCE_FILES fields_info.cpp sysdig.cpp win32/getopt.c) set(SOURCE_FILES_CSYSDIG fields_info.cpp csysdig.cpp win32/getopt.c) endif() add_executable(sysdig ${SOURCE_FILES}) add_executable(csysdig ${SOURCE_FILES_CSYSDIG}) if(NOT WIN32) include_directories(${PROJECT_BINARY_DIR}/driver/src) target_link_libraries(sysdig sinsp) if(USE_BUNDLED_NCURSES) add_dependencies(csysdig ncurses) endif() target_link_libraries(csysdig sinsp "${CURSES_LIBRARIES}") add_subdirectory(man) install(TARGETS sysdig DESTINATION bin) install(TARGETS csysdig DESTINATION bin) install(DIRECTORY chisels DESTINATION share/sysdig) file(COPY chisels DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") else() target_link_libraries(sysdig sinsp) target_link_libraries(csysdig sinsp) target_link_libraries(sysdig odbc32.lib odbccp32.lib) target_link_libraries(csysdig odbc32.lib odbccp32.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 "${ZLIB_INCLUDE}/zlib1.dll" "${PROJECT_BINARY_DIR}/$(Configuration)/") add_custom_command(TARGET sysdig POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_directory "${PROJECT_SOURCE_DIR}/userspace/sysdig/chisels" "${PROJECT_BINARY_DIR}/$(Configuration)/chisels") add_custom_command(TARGET sysdig POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_if_different $ "${PROJECT_BINARY_DIR}/$(Configuration)/sysdig.exe") add_custom_command(TARGET csysdig POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_if_different $ "${PROJECT_BINARY_DIR}/$(Configuration)/csysdig.exe") endif() configure_file(config_sysdig.h.in config_sysdig.h) sysdig-0.26.4/userspace/sysdig/chisels/000077500000000000000000000000001352731327100200375ustar00rootroot00000000000000sysdig-0.26.4/userspace/sysdig/chisels/COPYING000066400000000000000000000262641352731327100211040ustar00rootroot00000000000000The contents of the driver/ subdirectory are licensed separately--see COPYING.driver. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. sysdig-0.26.4/userspace/sysdig/chisels/ansiterminal.lua000066400000000000000000000044511352731327100232340ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] local pairs = pairs local tostring = tostring local setmetatable = setmetatable local schar = string.char local ansiterminal = {} local colors = { -- attributes reset = 0, clear = 0, bright = 1, dim = 2, underscore = 4, blink = 5, reverse = 7, hidden = 8, -- foreground black = 30, red = 31, green = 32, yellow = 33, blue = 34, magenta = 35, cyan = 36, white = 37, -- background onblack = 40, onred = 41, ongreen = 42, onyellow = 43, onblue = 44, onmagenta = 45, oncyan = 46, onwhite = 47, } local function makecolor(name, value) ansiterminal[name] = schar(27) .. '[' .. tostring(value) .. 'm' end function ansiterminal.enable_color(enable_colors) if enable_colors == true then for c, v in pairs(colors) do makecolor(c, v) end else for name, v in pairs(colors) do ansiterminal[name] = "" end end end function ansiterminal.clearscreen() io.write(schar(27) .. '[' .. "2J") end function ansiterminal.moveto(x, y) io.write(schar(27) .. '[' .. tostring(x) .. ";" .. tostring(y) .. 'H') end function ansiterminal.moveup(n) io.write(schar(27) .. '[' .. tostring(n) .. 'F') end function ansiterminal.clearline() io.write(schar(27) .. '[' .. "2K") end function ansiterminal.hidecursor() io.write(schar(27) .. '[' .. "?25l") end function ansiterminal.showcursor() io.write(schar(27) .. '[' .. "?25h") end function ansiterminal.setfgcol(color) io.write(schar(27) .. '[' .. "38;5;" .. color .. "m") end function ansiterminal.setbgcol(color) io.write(schar(27) .. '[' .. "48;5;" .. color .. "m") end return ansiterminal sysdig-0.26.4/userspace/sysdig/chisels/around.lua000066400000000000000000000070661352731327100220430ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Given a filter on the command line, this chisel saves the events that are in a time range around filter matches, and that are on the SAME process/thread. The time range can be adjusted with the dump_range_ms argument. For example, 'sysdig -c around evt.type=open and evt.failed=true' will save two seconds of activity around every failed open."; short_description = "Export to file the events around the time range where the given filter matches."; category = "Misc"; -- Argument list args = { { name = "dump_file_name", description = "The name of the file where the chisel will write the events related to each syslog entry.", argtype = "string", optional = false }, { name = "dump_range_ms", description = "The time interval to capture *before* and *after* each event, in milliseconds. For example, 500 means that 1 second around each displayed event (.5s before and .5s after) will be saved to . The default value for dump_range_ms is 1000.", argtype = "int", optional = true }, } -- Imports and globals require "common" terminal = require "ansiterminal" terminal.enable_color(true) local dump_file_name = nil local dump_range_ms = "1000" local entrylist = {} local capturing = false -- Argument notification callback function on_set_arg(name, val) if name == "dump_file_name" then dump_file_name = val return true elseif name == "dump_range_ms" then dump_range_ms = val return true end return false end -- Initialization callback function on_init() -- Request the fields that we need fpname = chisel.request_field("proc.name") ftid = chisel.request_field("thread.tid") fetime = chisel.request_field("evt.time") is_tty = sysdig.is_tty() if sysdig.get_filter() == "" then print("no filter specified") return false end return true end -- Final chisel initialization function on_capture_start() if sysdig.is_live() then print("live capture not supported") return false end capturing = true return true end -- Event parsing callback function on_event() -- Extract the event details local pname = evt.field(fpname) local tid = evt.field(ftid) local etime = evt.field(fetime) if pname == nil then pname = "" end print(etime .. " " .. pname .. "(" .. tid .. ")") local hi, low = evt.get_ts() local tid = evt.field(ftid) table.insert(entrylist, {hi, low, tid}) return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end() if is_tty then print(terminal.reset) end if capturing then local sn = sysdig.get_evtsource_name() local args = "-F -r" .. sn .. " -w" .. dump_file_name .. " " for i, v in ipairs(entrylist) do if i ~= 1 then args = args .. " or " end args = args .. "(evt.around[" .. ts_to_str(v[1], v[2]) .. "]=" .. dump_range_ms .. " and thread.tid=" .. v[3] .. ")" end print("\nSaving events around " .. #entrylist .. " syslog entries to " .. dump_file_name) sysdig.run_sysdig(args) end end sysdig-0.26.4/userspace/sysdig/chisels/bottlenecks.lua000066400000000000000000000050071352731327100230610ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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 number of items to show HOW_MANY = 10 -- Chisel description description = "Lists the " .. HOW_MANY .. " system calls that took the longest to return during the capture interval."; short_description = "Slowest system calls"; category = "Performance"; -- Chisel argument list args = {} slow_calls = {} last_lines = {} -- Initialization callback function on_init() -- Request the fields fevnum = chisel.request_field("evt.num") fevtime = chisel.request_field("evt.time") fevtype = chisel.request_field("evt.type") fevtargs = chisel.request_field("evt.args") flatency = chisel.request_field("evt.latency") fprname = chisel.request_field("proc.name") ftid = chisel.request_field("thread.tid") return true end -- Event parsing callback function on_event() latency = evt.field(flatency) tid = evt.field(ftid) evtype = evt.field(fevtype) if evtype == "switch" then return true end if latency == 0 then prname = evt.field(fprname) if prname == nil then prname = "" end line = string.format("%d) 0.%.9d %s (%d) > %s %s", evt.field(fevnum), 0, prname, evt.field(ftid), evtype, evt.field(fevtargs)) last_lines[tid] = line elseif latency ~= nil then for j = 1, HOW_MANY do if slow_calls[j] == nil or latency > slow_calls[j][1] then prname = evt.field(fprname) if prname == nil then prname = "" end line = string.format("%d) %d.%.9d %s (%d) < %s %s", evt.field(fevnum), latency / 1000000000, latency % 1000000000, prname, evt.field(ftid), evtype, evt.field(fevtargs)) table.insert(slow_calls, j, {latency, last_lines[tid], line}) break end end if #slow_calls > HOW_MANY then table.remove(slow_calls) end end return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end() for j = 1, #slow_calls do print(slow_calls[j][2]) print(slow_calls[j][3]) end return true end sysdig-0.26.4/userspace/sysdig/chisels/common.lua000066400000000000000000000175361352731327100220460ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] --[[ This file contains a bunch of functions that are helpful in multiple scripts ]]-- --[[ Serialize the content of a table into a tring ]]-- function st(val, name, skipnewlines, depth) skipnewlines = skipnewlines or false depth = depth or 0 local tmp = string.rep(" ", depth) if name then tmp = tmp .. name .. " = " end if type(val) == "table" then tmp = tmp .. "{" .. (not skipnewlines and "\n" or "") for k, v in pairs(val) do tmp = tmp .. st(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "") end tmp = tmp .. string.rep(" ", depth) .. "}" elseif type(val) == "number" then tmp = tmp .. tostring(val) elseif type(val) == "string" then tmp = tmp .. string.format("%q", val) elseif type(val) == "boolean" then tmp = tmp .. (val and "true" or "false") else tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\"" end return tmp end --[[ Extends a string to newlen with spaces ]]-- function extend_string(s, newlen) if #s < newlen then local ccs = " " s = s .. string.sub(ccs, 0, newlen - #s) return s else if newlen > 0 then return (string.sub(s, 0, newlen - 1) .. " ") else return "" end end end --[[ Basic string split. ]]-- function split(s, delimiter) local result = {} for match in (s..delimiter):gmatch("(.-)"..delimiter) do table.insert(result, match) end return result end --[[ Substring matching. ]]-- function starts_with(str, prefix) return prefix == "" or str:sub(1, #prefix) == prefix end function ends_with(str, suffix) return suffix == "" or str:sub(-#suffix) == suffix end --[[ convert a number into a byte representation. E.g. 1230 becomes 1.23K ]]-- function format_bytes(val) if val > (1024 * 1024 * 1024 * 1024 * 1024) then return string.format("%.2fP", val / (1024 * 1024 * 1024 * 1024 * 1024)) elseif val > (1024 * 1024 * 1024 * 1024) then return string.format("%.2fT", val / (1024 * 1024 * 1024 * 1024)) elseif val > (1024 * 1024 * 1024) then return string.format("%.2fG", val / (1024 * 1024 * 1024)) elseif val > (1024 * 1024) then return string.format("%.2fM", val / (1024 * 1024)) elseif val > 1024 then return string.format("%.2fKB", val / (1024)) else return string.format("%dB", val) end end --[[ convert a nanosecond time interval into a s.ns representation. E.g. 1100000000 becomes 1.1s ]]-- ONE_S_IN_NS=1000000000 ONE_MS_IN_NS=1000000 ONE_US_IN_NS=1000 function format_time_interval(val) if val >= (ONE_S_IN_NS) then return string.format("%u.%02us", math.floor(val / ONE_S_IN_NS), (val % ONE_S_IN_NS) / 10000000) elseif val >= (ONE_S_IN_NS / 100) then return string.format("%ums", math.floor(val / (ONE_S_IN_NS / 1000))) elseif val >= (ONE_S_IN_NS / 1000) then return string.format("%u.%02ums", math.floor(val / (ONE_S_IN_NS / 1000)), (val % ONE_MS_IN_NS) / 10000) elseif val >= (ONE_S_IN_NS / 100000) then return string.format("%uus", math.floor(val / (ONE_S_IN_NS / 1000000))) elseif val >= (ONE_S_IN_NS / 1000000) then return string.format("%u.%02uus", math.floor(val / (ONE_S_IN_NS / 1000000)), (val % ONE_US_IN_NS) / 10) else return string.format("%uns", val) end end --[[ extract the top num entries from the table t, after sorting them based on the entry value using the function order() ]]-- function pairs_top_by_val(t, num, order) local keys = {} for k in pairs(t) do keys[#keys+1] = k end table.sort(keys, function(a,b) return order(t, a, b) end) local i = 0 return function() i = i + 1 if (num == 0 or i <= num) and keys[i] then return keys[i], t[keys[i]] end end end --[[ Timestamp <-> string conversion ]]-- function ts_to_str(tshi, tslo) return string.format("%u%.9u", tshi, tslo) end --[[ Pick a key-value table and render it to the console in sorted top format ]]-- json = require ("dkjson") function print_sorted_table(stable, ts_s, ts_ns, timedelta, viz_info) local sorted_grtable = pairs_top_by_val(stable, viz_info.top_number, function(t,a,b) return t[b] < t[a] end) if viz_info.output_format == "json" then local jdata = {} local j = 1 for k,v in sorted_grtable do local vals = split(k, "\001\001") vals[#vals + 1] = v jdata[j] = vals j = j + 1 end local jinfo = {} for i, keyname in ipairs(viz_info.key_fld) do jinfo[i] = {name = keyname, desc = viz_info.key_desc[i], is_key = true} end jinfo[3] = {name = viz_info.value_fld, desc = viz_info.value_desc, is_key = false} local res = {ts = sysdig.make_ts(ts_s, ts_ns), data = jdata, info = jinfo} local str = json.encode(res, { indent = true }) print(str) else -- Same size to extend each string local EXTEND_STRING_SIZE = 20 local header = extend_string(viz_info.value_desc, EXTEND_STRING_SIZE) for i, fldname in ipairs(viz_info.key_desc) do header = header .. extend_string(fldname, EXTEND_STRING_SIZE) end print(header) print("--------------------------------------------------------------------------------") for k,v in sorted_grtable do local keystr = "" local singlekeys = split(k, "\001\001") for i, singlekey in ipairs(singlekeys) do if i < #singlekeys then keystr = keystr .. extend_string(string.sub(singlekey, 0, EXTEND_STRING_SIZE), EXTEND_STRING_SIZE) else keystr = keystr .. singlekey end end if viz_info.value_units == "none" then print(extend_string(tostring(v), EXTEND_STRING_SIZE) .. keystr) elseif viz_info.value_units == "bytes" then print(extend_string(format_bytes(v), EXTEND_STRING_SIZE) .. keystr) elseif viz_info.value_units == "time" then print(extend_string(format_time_interval(v), EXTEND_STRING_SIZE) .. keystr) elseif viz_info.value_units == "timepct" then if timedelta > 0 then pctstr = string.format("%.2f%%", v / timedelta * 100) else pctstr = "0.00%" end print(extend_string(pctstr, EXTEND_STRING_SIZE) .. keystr) end end end end --[[ Try to convert user input to a number using tonumber(). If tonumber() returns 'nil', print an error message to the user and exit, otherwise return tonumber(value). ]]-- function parse_numeric_input(value, name) val = tonumber(value) if val == nil then print(string.format("Input %s must be a number.", name)) require ("os") os.exit() end return val end --[[ Perform a deep copy of a table. ]]-- function copytable(orig) local orig_type = type(orig) local copy if orig_type == 'table' then copy = {} for orig_key, orig_value in next, orig, nil do copy[copytable(orig_key)] = copytable(orig_value) end setmetatable(copy, copytable(getmetatable(orig))) else -- number, string, boolean, etc copy = orig end return copy end --[[ Add the content of a table at the end of another one. ]]-- function concattable(dst, src) for i=1,#src do dst[#dst + 1] = src[i] end return dst end --[[ return the type of a variable. ]]-- function typeof(var) local _type = type(var); if(_type ~= "table" and _type ~= "userdata") then return _type; end local _meta = getmetatable(var); if(_meta ~= nil and _meta._NAME ~= nil) then return _meta._NAME; else return _type; end end sysdig-0.26.4/userspace/sysdig/chisels/dkjson.lua000066400000000000000000000637401352731327100220440ustar00rootroot00000000000000 -- Module options: local always_try_using_lpeg = true local register_global_module_table = false local global_module_name = 'json' --[==[ David Kolf's JSON module for Lua 5.1/5.2 ======================================== *Version 2.4* In the default configuration this module writes no global values, not even the module table. Import it using json = require ("dkjson") In environments where `require` or a similar function are not available and you cannot receive the return value of the module, you can set the option `register_global_module_table` to `true`. The module table will then be saved in the global variable with the name given by the option `global_module_name`. Exported functions and values: `json.encode (object [, state])` -------------------------------- Create a string representing the object. `Object` can be a table, a string, a number, a boolean, `nil`, `json.null` or any object with a function `__tojson` in its metatable. A table can only use strings and numbers as keys and its values have to be valid objects as well. It raises an error for any invalid data types or reference cycles. `state` is an optional table with the following fields: - `indent` When `indent` (a boolean) is set, the created string will contain newlines and indentations. Otherwise it will be one long line. - `keyorder` `keyorder` is an array to specify the ordering of keys in the encoded output. If an object has keys which are not in this array they are written after the sorted keys. - `level` This is the initial level of indentation used when `indent` is set. For each level two spaces are added. When absent it is set to 0. - `buffer` `buffer` is an array to store the strings for the result so they can be concatenated at once. When it isn't given, the encode function will create it temporary and will return the concatenated result. - `bufferlen` When `bufferlen` is set, it has to be the index of the last element of `buffer`. - `tables` `tables` is a set to detect reference cycles. It is created temporary when absent. Every table that is currently processed is used as key, the value is `true`. When `state.buffer` was set, the return value will be `true` on success. Without `state.buffer` the return value will be a string. `json.decode (string [, position [, null]])` -------------------------------------------- Decode `string` starting at `position` or at 1 if `position` was omitted. `null` is an optional value to be returned for null values. The default is `nil`, but you could set it to `json.null` or any other value. The return values are the object or `nil`, the position of the next character that doesn't belong to the object, and in case of errors an error message. Two metatables are created. Every array or object that is decoded gets a metatable with the `__jsontype` field set to either `array` or `object`. If you want to provide your own metatables use the syntax json.decode (string, position, null, objectmeta, arraymeta) To prevent the assigning of metatables pass `nil`: json.decode (string, position, null, nil) `.__jsonorder` ------------------------- `__jsonorder` can overwrite the `keyorder` for a specific table. `.__jsontype` ------------------------ `__jsontype` can be either `"array"` or `"object"`. This value is only checked for empty tables. (The default for empty tables is `"array"`). `.__tojson (self, state)` ------------------------------------ You can provide your own `__tojson` function in a metatable. In this function you can either add directly to the buffer and return true, or you can return a string. On errors nil and a message should be returned. `json.null` ----------- You can use this value for setting explicit `null` values. `json.version` -------------- Set to `"dkjson 2.4"`. `json.quotestring (string)` --------------------------- Quote a UTF-8 string and escape critical characters using JSON escape sequences. This function is only necessary when you build your own `__tojson` functions. `json.addnewline (state)` ------------------------- When `state.indent` is set, add a newline to `state.buffer` and spaces according to `state.level`. LPeg support ------------ When the local configuration variable `always_try_using_lpeg` is set, this module tries to load LPeg to replace the `decode` function. The speed increase is significant. You can get the LPeg module at . When LPeg couldn't be loaded, the pure Lua functions stay active. In case you don't want this module to require LPeg on its own, disable the option `always_try_using_lpeg` in the options section at the top of the module. In this case you can later load LPeg support using ### `json.use_lpeg ()` Require the LPeg module and replace the functions `quotestring` and and `decode` with functions that use LPeg patterns. This function returns the module table, so you can load the module using: json = require "dkjson".use_lpeg() Alternatively you can use `pcall` so the JSON module still works when LPeg isn't found. json = require "dkjson" pcall (json.use_lpeg) ### `json.using_lpeg` This variable is set to `true` when LPeg was loaded successfully. --------------------------------------------------------------------- Contact ------- You can contact the author by sending an e-mail to 'david' at the domain 'dkolf.de'. --------------------------------------------------------------------- *Copyright (C) 2010-2013 David Heiko Kolf* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. sysdig-0.26.4/userspace/sysdig/chisels/echo_fds.lua000066400000000000000000000100431352731327100223120ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Print the data read and written for any FD. Combine this script with a filter to restrict what it shows. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown. (Blue represents [Write], and Red represents [Read] for all data except when the -pc or -pcontainer argument is used. If used the container.name and container.id will be represented as: Green [host], and Cyan [container]) Container information will contain '[]' around container.name and container.id."; short_description = "Print the data read and written by processes."; category = "I/O"; args = { { name = "disable_color", description = "Set to 'disable_colors' if you want to disable color output", argtype = "string", optional = true }, } require "common" terminal = require "ansiterminal" terminal.enable_color(true) -- Argument notification callback function on_set_arg(name, val) if name == "disable_color" and val == "disable_color" then terminal.enable_color(false) end return true end -- Initialization callback function on_init() -- Request the fields that we need fbuf = chisel.request_field("evt.rawarg.data") fisread = chisel.request_field("evt.is_io_read") fres = chisel.request_field("evt.rawarg.res") fname = chisel.request_field("fd.name") fpname = chisel.request_field("proc.name") fcontainername = chisel.request_field("container.name") fcontainerid = chisel.request_field("container.id") -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() -- increase the snaplen so we capture more of the conversation sysdig.set_snaplen(2000) -- set the filter chisel.set_filter("evt.is_io=true and evt.dir=< and evt.rawres>0") chisel.set_event_formatter("%evt.arg.data") return true end -- Event parsing callback function on_event() local buf = evt.field(fbuf) local isread = evt.field(fisread) local res = evt.field(fres) local name = evt.field(fname) local pname = evt.field(fpname) local containername = evt.field(fcontainername) local containerid = evt.field(fcontainerid) if name == nil then name = "" end if res <= 0 then return true end local container = "" if print_container then if containername == "host" then -- Make host green container = string.format("%s [%s] [%s]", terminal.green, containername, containerid ); else -- Make container cyan container = string.format("%s [%s] [%s]", terminal.cyan, containername, containerid ); end end if isread then -- Because container info might be colored make the end of the line the same color as read (red) name_pname = string.format("%s %s (%s)", terminal.red, name, pname ); -- When a read occurs show it in red infostr = string.format("%s------ Read %s from %s %s", terminal.red, format_bytes(res), container, name_pname) else -- Because container info might be colored make the end of the line the same color as write (blue) name_pname = string.format("%s %s (%s)", terminal.blue, name, pname ); -- When a write occurs show it in blue infostr = string.format("%s------ Write %s to %s %s", terminal.blue, format_bytes(res), container, name_pname) end -- Print out the line (if -pc or -pcontainer sandwitch container color between either red of blue) print(infostr) return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end() print(terminal.reset) end sysdig-0.26.4/userspace/sysdig/chisels/fdbytes_by.lua000066400000000000000000000027621352731327100227030ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Groups FD activity based on the given filter field, and returns the key that generated the most input+output bytes. For example, this script can be used to list the processes or TCP ports that generated most traffic." short_description = "I/O bytes, aggregated by an arbitrary filter field" category = "I/O" -- Chisel argument list args = { { name = "key", description = "The filter field used for grouping", argtype = "string" }, } -- The number of items to show TOP_NUMBER = 30 key_fld = "" -- Argument notification callback function on_set_arg(name, val) if name == "key" then key_fld = val return true end return false end -- Initialization callback function on_init() chisel.exec("table_generator", key_fld, key_fld, "evt.rawarg.res", "Bytes", "evt.is_io=true and evt.failed=false", "" .. TOP_NUMBER, "bytes") return true end sysdig-0.26.4/userspace/sysdig/chisels/fdcount_by.lua000066400000000000000000000045021352731327100226770ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Groups all the active FDs based on the given filter field, and returns the fd count for each key. For example, it can be used to list the number of connections per process or per IP endpoint." short_description = "FD count, aggregated by an arbitrary filter field" category = "I/O" -- Chisel argument list args = { { name = "key", description = "The filter field used for grouping", argtype = "string" }, } -- The number of items to show TOP_NUMBER = 0 require "common" grtable = {} key_fld = "" -- Argument notification callback function on_set_arg(name, val) if name == "key" then key_fld = val return true end return false end -- Initialization callback function on_init() -- Request the fields we need fkey = chisel.request_field(key_fld) ffdnum = chisel.request_field("fd.num") ffdname = chisel.request_field("fd.name") return true end -- Event parsing callback function on_event() key = evt.field(fkey) fdnum = evt.field(ffdnum) fdname = evt.field(ffdname) if key ~= nil and fdnum ~= nil and fdnum > 0 and fdname ~= nil and fdname ~= "" then entryval = grtable[key] fdkey = tostring(fdnum) .. fdname if entryval == nil then grtable[key] = {} grtable[key][fdkey] = 1 grtable[key]["c"] = 1 else fdentry = grtable[key][fdkey] if fdentry == nil then grtable[key][fdkey] = 1 grtable[key]["c"] = grtable[key]["c"] + 1 end end end return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end() sorted_grtable = pairs_top_by_val(grtable, TOP_NUMBER, function(t,a,b) return t[b]["c"] < t[a]["c"] end) etime = evt.field(ftime) for k,v in sorted_grtable do print(k, v["c"]) end return true end sysdig-0.26.4/userspace/sysdig/chisels/fdtime_by.lua000066400000000000000000000026531352731327100225120ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Groups FD activity based on the given filter field, and returns the keys where most time was spent. For example, this script can be used to list the processes or files that caused the biggest I/O latency." short_description = "FD time group by" category = "I/O" -- Chisel argument list args = { { name = "key", description = "The filter field used for grouping", argtype = "string" }, } -- The number of items to show TOP_NUMBER = 10 key_fld = "" -- Argument notification callback function on_set_arg(name, val) if name == "key" then key_fld = val return true end return false end -- Initialization callback function on_init() chisel.exec("table_generator", key_fld, key_fld, "evt.latency", "Time", "evt.is_io=true", "" .. TOP_NUMBER, "time") return true end sysdig-0.26.4/userspace/sysdig/chisels/fileslower.lua000066400000000000000000000116441352731327100227230ustar00rootroot00000000000000--[[ Copyright (C) 2014 Brendan Gregg. This file is part of sysdig. 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. --]] --[[ fileslower.lua - trace file I/O slower than a given threshold. USAGE: sysdig -c fileslower min_ms eg, sysdig -c fileslower 10 # show file I/O slower than 10 ms sysdig -c fileslower 0 # show all file I/O sysdig -c fileslower "1 disable_colors" # show file I/O slower than 1 ms. w/ no colors sysdig -pc -c fileslower 0 # show all file I/O and container output By default this skips file I/O to /dev. Modify the skip_dev variable in this chisel to change this behavior. Note: The file I/O traced is those matched by the sysdig filter: "evt.is_io=true and fd.type=file". --]] -- Chisel description description = "Trace file I/O slower than a threshold, or all file I/O. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown. (Blue represents a process running within a container, and Green represents a host process)"; short_description = "Trace slow file I/O"; category = "Performance"; skip_dev = true -- skip /dev/... files -- Chisel argument list args = { { name = "min_ms", description = "Minimum millisecond threshold for showing file I/O", argtype = "int", optional = false }, { name = "disable_color", description = "Set to 'disable_colors' if you want to disable color output", argtype = "string", optional = true }, } require "common" terminal = require "ansiterminal" terminal.enable_color(true) -- Argument notification callback function on_set_arg(name, val) if name == "disable_color" and val == "disable_color" then terminal.enable_color(false) elseif name == "min_ms" then min_ms = parse_numeric_input(val, name) end return true end -- Initialization callback function on_init() -- set the following fields on_event() etype = chisel.request_field("evt.type") dir = chisel.request_field("evt.dir") datetime = chisel.request_field("evt.datetime") fname = chisel.request_field("fd.name") pname = chisel.request_field("proc.name") latency = chisel.request_field("evt.latency") fcontainername = chisel.request_field("container.name") fcontainerid = chisel.request_field("container.id") -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() -- filter for file I/O chisel.set_filter("evt.is_io=true and fd.type=file") -- The -pc or -pcontainer options was supplied on the cmd line if print_container then print(string.format("%-23.23s %-20.20s %-20.20s %-12.12s %-8s %-12s %s", "evt.datetime", "container.id", "container.name", "proc.name", "evt.type", "LATENCY(ms)", "fd.name")) print(string.format("%-23.23s %-20.20s %-20.20s %-12.12s %-8s %-12s %s", "-----------------------", "------------------------------", "------------------------------", "------------", "--------", "------------", "-----------------------------------------")) else print(string.format("%-23.23s %-12.12s %-8s %-12s %s", "evt.datetime", "proc.name", "evt.type", "LATENCY(ms)", "fd.name")) print(string.format("%-23.23s %-12.12s %-8s %-12s %s", "-----------------------", "------------", "--------", "------------", "-----------------------------------------")) end return true end -- Event callback function on_event() local color = terminal.green lat = evt.field(latency) / 1000000 fn = evt.field(fname) if evt.field(dir) == "<" and lat > min_ms then -- filter /dev files if needed if skip_dev == false or string.sub(fn, 0, 5) ~= "/dev/" then -- If this is a container modify the output color if evt.field(fcontainername) ~= "host" then color = terminal.blue end -- The -pc or -pcontainer options was supplied on the cmd line if print_container then print(color .. string.format("%-23.23s %-20.20s %-20.20s %-12.12s %-8s %12d %s", evt.field(datetime), evt.field(fcontainerid), evt.field(fcontainername), evt.field(pname), evt.field(etype), lat, fn )) else print(color .. string.format("%-23.23s %-12.12s %-8s %12d %s", evt.field(datetime), evt.field(pname), evt.field(etype), lat, fn )) end end end return true end sysdig-0.26.4/userspace/sysdig/chisels/flame.lua000066400000000000000000000300121352731327100216220ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description disabled_description = "Flame graph generator"; short_description = "Sysdig trace flame graph builder"; category = "Performance"; -- Chisel argument list args = { } require "common" json = require ("dkjson") local CAPTURE_LOGS = true local spans = {} local fid local flatency local fcontname local fexe local fbuf local fdir local ftime local MAX_DEPTH = 256 local avg_tree = {} local full_tree = {} local max_tree = {} local min_tree = {} local logs_tree = {} local next = next -- make next faster local PAGE_HEADER = [[ Flame UI ]] -- Argument notification callback function on_set_arg(name, val) return true end -- Initialization callback function on_init() -- Request the fields needed for this chisel for j = 0, MAX_DEPTH do local fname = "span.tag[" .. j .. "]" local minfo = chisel.request_field(fname) spans[j] = minfo end fid = chisel.request_field("span.id") flatency = chisel.request_field("span.duration") fcontname = chisel.request_field("container.name") fexe = chisel.request_field("proc.exeline") fbuf = chisel.request_field("evt.buffer") fdir = chisel.request_field("evt.dir") ftid = chisel.request_field("thread.tid") ftime = chisel.request_field("evt.time") -- set the filter if CAPTURE_LOGS then chisel.set_filter("(evt.type=tracer) or (evt.is_io_write=true and evt.dir=< and (fd.num=1 or fd.num=2 or fd.name contains log))") else chisel.set_filter("evt.type=tracer and evt.dir=<") end return true end -- Add a log entry into the proper place(s) in the log table function collect_log(tid_tree) for k,entry in pairs(tid_tree) do while true do local lastv = v k,v = next(entry) if v == nil then if lastv.l == nil then lastv.l = {} end local etime = evt.field(ftime) local buf = evt.field(fbuf) local tid = evt.field(ftid) local hi, low = evt.get_ts() local linedata = {t=etime, th=hi, tl=low, tid=tid, b=buf} table.insert(lastv.l, linedata) --print("*** " .. evt.get_num() .. " " .. linedata) --print(st(logs_tree)) --print("***************************") return end entry = v.ch end end end -- Parse a tracer enter event and update the logs_tree table function parse_tracer_enter(logtable_cur, hr) for j = 1, #hr do local mv = hr[j] if mv == nil then break end if logtable_cur[mv] == nil then logtable_cur[mv] = {ch={}} end if j == #hr then logtable_cur[mv].r=true end logtable_cur = logtable_cur[mv].ch end end -- Parse a tracer exit event and update the given transaction entry function parse_tracer_exit(mrk_cur, logtable_cur, hr, latency, contname, exe, id) local res = false local parent_has_logs = false; for j = 1, #hr do local mv = hr[j] if mv == nil or mrk_cur == nil then break end local has_logtable_entry = (logtable_cur ~= nil and logtable_cur[mv] ~= nil) --print("! " .. evt.get_num() .. " " .. j) --print(parent_has_logs) --print(logtable_cur[mv].r) if j == #hr then local llogs if has_logtable_entry and logtable_cur[mv].l ~= nil then llogs = logtable_cur[mv].l else llogs = nil end --print("################ " .. evt.get_num() .. " " .. st(logs_tree)) if mrk_cur[mv] == nil then mrk_cur[mv] = {t=latency, tt=latency, cont=contname, exe=exe, c=1, logs=llogs} if j == 1 then mrk_cur[mv].n = 0 end else mrk_cur[mv]["tt"] = mrk_cur[mv]["tt"] + latency mrk_cur[mv]["cont"] = contname mrk_cur[mv]["exe"] = exe mrk_cur[mv]["c"] = 1 mrk_cur[mv]["logs"] = llogs end --print("################ " .. evt.get_num()) --print(st(logs_tree)) --print("## " .. evt.get_num()) --print(st(logtable_cur[mv].r)) if has_logtable_entry and parent_has_logs == false then res = true else logtable_cur[mv] = nil has_logtable_entry = false logtable_cur = nil end elseif j == (#hr - 1) then if mrk_cur[mv] == nil then mrk_cur[mv] = {tt=0} if j == 1 then mrk_cur[mv].n = 0 end end else if mrk_cur[mv] == nil then mrk_cur[mv] = {tt=0} if j == 1 then mrk_cur[mv].n = 0 mrk_cur[mv]["id"] = id end end end if mrk_cur[mv]["ch"] == nil then mrk_cur[mv]["ch"] = {} end if #hr == 1 then mrk_cur[mv].n = mrk_cur[mv].n + 1 end -- end of node parsing, update pointers to movo to the child if has_logtable_entry then parent_has_logs = (logtable_cur[mv].r ~= nil) end mrk_cur = mrk_cur[mv].ch if logtable_cur ~= nil then logtable_cur = logtable_cur[mv].ch end end return res end -- Event parsing callback function on_event() local etype = evt.get_type() if etype ~= "tracer" then local tid = evt.field(ftid) if logs_tree[tid] == nil then return else collect_log(logs_tree[tid]) end return end local latency = evt.field(flatency) local contname = evt.field(fcontname) local id = evt.field(fid) local exe = evt.field(fexe) local hr = {} local full_trs = nil local dir = evt.field(fdir) local tid = evt.field(ftid) for j = 0, MAX_DEPTH do hr[j + 1] = evt.field(spans[j]) end if dir == ">" then if logs_tree[tid] == nil then logs_tree[tid] = {} end local idt = logs_tree[tid][id] if idt == nil then logs_tree[tid][id] = {} idt = logs_tree[tid][id] end parse_tracer_enter(idt, hr) return true else if latency == nil then return true end if full_tree[id] == nil then full_tree[id] = {} end -- find the logs for this transaction span local logs if logs_tree[tid] == nil then logs = nil else if logs_tree[tid][id] == nil then logs = nil else logs = logs_tree[tid][id] end end if parse_tracer_exit(full_tree[id], logs, hr, latency, contname, exe, id) then --print(st(logs_tree)) --print("------------ " .. evt.get_num()) --print(st(full_tree)) --print("---------------------------------------------------") logs_tree[tid][id] = nil if next(logs_tree[tid]) == nil then logs_tree[tid] = nil end end return true end end function calculate_t_in_node(node) local totchtime = 0 local maxchtime = 0 local nconc = 0 local ch_to_keep if node.ch then for k,d in pairs(node.ch) do local nv = calculate_t_in_node(d) totchtime = totchtime + nv if nv > maxchtime then maxchtime = nv ch_to_keep = d end nconc = nconc + 1 end end if node.tt >= totchtime then node.t = node.tt - totchtime else node.t = node.tt - maxchtime node.nconc = nconc for k,d in pairs(node.ch) do if d ~= ch_to_keep then node.ch[k] = nil end end end return node.tt end function normalize(node, factor) node.t = node.t / factor node.tt = node.tt / factor if node.ch then for k,d in pairs(node.ch) do normalize(d, factor) end end end function is_transaction_complete(node) if node.c ~= 1 then return false end if node.ch then for k,d in pairs(node.ch) do if is_transaction_complete(d) == false then return false end end end return true end function update_avg_tree(dsttree, key, val) if dsttree[key] == nil then dsttree[key] = copytable(val) return else dsttree[key].tt = dsttree[key].tt + val.tt if dsttree[key].n then dsttree[key].n = dsttree[key].n + 1 end if val.logs then if dsttree[key].logs == nil then dsttree[key].logs = {} end concattable(dsttree[key].logs, val.logs) end end if val.ch then if dsttree[key].ch == nil then dsttree[key].ch = {} end for k,d in pairs(val.ch) do update_avg_tree(dsttree[key].ch, k, d) end end end function update_max_tree(dsttree, key, val) if dsttree[key] == nil then dsttree[key] = val return else if val.tt > dsttree[key].tt then dsttree[key] = val end end end function update_min_tree(dsttree, key, val) if dsttree[key] == nil then dsttree[key] = val return else if val.tt < dsttree[key].tt then dsttree[key] = val end end end -- This processes the transaction list to extract and aggregate the transactions to emit function collapse_tree() -- scan the transaction list for i,v in pairs(full_tree) do local ttt = 0 for key,val in pairs(v) do ttt = ttt + val.tt if is_transaction_complete(val) then update_avg_tree(avg_tree, key, val) update_max_tree(max_tree, key, val) update_min_tree(min_tree, key, val) end end end end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end() --print(st(full_tree)) -- Process the list and create the required transactions collapse_tree() -- calculate the unique time spent in each node for i,v in pairs(avg_tree) do calculate_t_in_node(v) end -- normalize each root span tree for i,v in pairs(avg_tree) do normalize(v, v.n) end print(PAGE_HEADER) -- emit the average transaction local AvgData = {} AvgData[""] = {ch=avg_tree, t=0, tt=0} local str = json.encode(AvgData, { indent = true }) print('"avg": ' .. str .. ",") -- normalize the best transaction for i,v in pairs(min_tree) do calculate_t_in_node(v) end -- emit the best transaction local tdata = {} tdata[""] = {ch=min_tree, t=0, tt=0} local str = json.encode(tdata, { indent = true }) print('"min": ' .. str .. ",") -- normalize the worst transaction for i,v in pairs(max_tree) do calculate_t_in_node(v) end -- emit the worst transaction local tdata = {} tdata[""] = {ch=max_tree, t=0, tt=0} local str = json.encode(tdata, { indent = true }) print('"max": ' .. str .. ",") print(PAGE_TRAILER) end sysdig-0.26.4/userspace/sysdig/chisels/http.lua000066400000000000000000000060721352731327100215260ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Common function used by http parsing chisels partial_transactions = {} function http_init() chisel.set_filter("evt.is_io = true and evt.buflen > 0 and (fd.sockfamily = ip or fd.sockfamily = unix)") buffer_field = chisel.request_field("evt.buffer") fd_field = chisel.request_field("fd.num") pid_field = chisel.request_field("proc.pid") rawtime_field = chisel.request_field("evt.rawtime") datetime_field = chisel.request_field("evt.datetime") dir_field = chisel.request_field("evt.io_dir") container_field = chisel.request_field("container.name") sysdig.set_snaplen(1024) end function parse_request(req_buffer) method, url = string.match(req_buffer, "^(%u+) (%g+)") if method and url then host = string.match(req_buffer, "Host: (%g+)%.%.") if host then url = host .. url end return { method=method, url=url } end return nil end function parse_response(resp_buffer) resp_code = string.match(resp_buffer, "HTTP/[%g]+ (%d+)") if resp_code then content_length = string.match(resp_buffer, "Content%-Length: (%d+)%.%.") if not content_length then content_length = 0 end return { code = tonumber(resp_code), length = tonumber(content_length) } else return nil end end function run_http_parser(evt, on_transaction) buf = evt.field(buffer_field) fd = evt.field(fd_field) pid = evt.field(pid_field) evt_dir = evt.field(dir_field) key = string.format("%d\001\001%d", pid, fd) timestamp = evt.field(rawtime_field) transaction = partial_transactions[key] if not transaction then request = parse_request(buf) if request then transaction_dir = "" if evt_dir == "read" then transaction_dir = "<" elseif evt_dir == "write" then transaction_dir = ">" end request["ts"] = timestamp partial_transactions[key] = { request= request, dir=transaction_dir, container=evt.field(container_field) } end else response = parse_response(buf) if response then transaction["response"] = response transaction["response"]["ts"] = timestamp on_transaction(transaction) partial_transactions[key] = nil end end end sysdig-0.26.4/userspace/sysdig/chisels/httplog.lua000066400000000000000000000032631352731327100222270ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Show a log of all HTTP requests"; short_description = "HTTP requests log"; category = "Application"; args = {} require "http" -- Initialization callback function on_init() http_init() -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() return true end function on_transaction(transaction) if print_container then container = " " .. transaction["container"] .. " " else container = " " end print(string.format("%s%s%s method=%s url=%s response_code=%d latency=%dms size=%dB", evt.field(datetime_field), container, transaction["dir"], transaction["request"]["method"], transaction["request"]["url"], transaction["response"]["code"], (transaction["response"]["ts"] - transaction["request"]["ts"])/1000000, transaction["response"]["length"] )) end function on_event() run_http_parser(evt, on_transaction) end sysdig-0.26.4/userspace/sysdig/chisels/httptop.lua000066400000000000000000000112301352731327100222410ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Show top HTTP requests by: ncalls, time or bytes"; short_description = "Top HTTP requests"; category = "Application"; -- Chisel argument list args = { { name = "by", description = "Show top HTTP transactions by: ncalls, time or bytes, default is ncalls", argtype = "string", optional = true }, } require "common" terminal = require "ansiterminal" require "http" vizinfo = { key_fld = {"method", "url"}, key_desc = {"method", "url"}, value_fld = "ncalls", value_desc = "ncalls", value_units = "none", top_number = 30, output_format = "normal" } by_field = "ncalls" -- Argument notification callback function on_set_arg(name, val) if name == "by" then if val == "time" then vizinfo["value_fld"] = "time" vizinfo["value_desc"] = "time" vizinfo["value_units"] = "time" elseif val == "ncalls" then vizinfo["value_fld"] = "ncalls" vizinfo["value_desc"] = "ncalls" vizinfo["value_units"] = "none" elseif val == "bytes" then vizinfo["value_fld"] = "bytes" vizinfo["value_desc"] = "bytes" vizinfo["value_units"] = "bytes" else print("Invalid argument! Valid options: ncalls, bytes, time") return false end by_field = val return true end end -- Initialization callback function on_init() http_init() -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() if print_container then table.insert(vizinfo["key_fld"], 1, "container") table.insert(vizinfo["key_desc"], 1, "container") end return true end islive = false grtable = {} partial_transactions = {} function build_grtable_key(transaction) request = transaction["request"] ret = "" if print_container then ret = transaction["container"] .. "\001\001" end ret = ret .. string.format("%s\001\001%s", request["method"], request["url"]) return ret end function on_transaction(transaction) grtable_key = build_grtable_key(transaction) if not grtable[grtable_key] then grtable[grtable_key] = {} end table.insert(grtable[grtable_key], transaction) end function on_event() run_http_parser(evt, on_transaction) end -- Final chisel initialization function on_capture_start() islive = sysdig.is_live() vizinfo.output_format = sysdig.get_output_format() if islive then chisel.set_interval_s(1) if vizinfo.output_format ~= "json" then terminal.clearscreen() terminal.hidecursor() end end return true end function aggregate_grtable() for key, transactions in pairs(grtable) do if by_field == "ncalls" then grtable[key] = #transactions elseif by_field == "bytes" then total_bytes = 0 for _, tr in ipairs(transactions) do total_bytes = total_bytes + tr["response"]["length"] end grtable[key] = total_bytes elseif by_field == "time" then total_time = 0 for _, tr in ipairs(transactions) do total_time = total_time + tr["response"]["ts"] - tr["request"]["ts"] end grtable[key] = total_time / #transactions end end end function on_interval(ts_s, ts_ns, delta) if vizinfo.output_format ~= "json" then terminal.clearscreen() terminal.moveto(0, 0) end aggregate_grtable() print_sorted_table(grtable, ts_s, 0, delta, vizinfo) -- Clear the table grtable = {} return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end(ts_s, ts_ns, delta) if islive and vizinfo.output_format ~= "json" then terminal.clearscreen() terminal.moveto(0 ,0) terminal.showcursor() return true end aggregate_grtable() print_sorted_table(grtable, ts_s, 0, delta, vizinfo) return true end sysdig-0.26.4/userspace/sysdig/chisels/iobytes.lua000066400000000000000000000032201352731327100222150ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Counts the total bytes read from and written to any type of FD (disk, socket, pipe...) and prints the result every second."; short_description = "Sum of I/O bytes on any type of FD"; category = "I/O"; -- Chisel argument list args = { } tot = 0 totin = 0 totout = 0 -- Initialization callback function on_init() -- Request the fields fbytes = chisel.request_field("evt.rawarg.res") ftime = chisel.request_field("evt.time.s") fisread = chisel.request_field("evt.is_io_read") -- set the filter chisel.set_filter("evt.is_io=true") chisel.set_interval_s(1) return true end -- Event parsing callback function on_event() bytes = evt.field(fbytes) isread = evt.field(fisread) if bytes ~= nil and bytes > 0 then tot = tot + bytes if isread then totin = totin + bytes else totout = totout + bytes end end return true end function on_interval(delta) etime = evt.field(ftime) print(etime .. " in:" .. totin .. " out:" .. totout .. " tot:" .. tot) tot = 0 totin = 0 totout = 0 return true end sysdig-0.26.4/userspace/sysdig/chisels/iobytes_file.lua000066400000000000000000000031201352731327100232130ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Counts the total bytes read from and written to files."; short_description = "Sum of file I/O bytes"; category = "I/O"; -- Chisel argument list args = { } tot = 0 totin = 0 totout = 0 -- Initialization callback function on_init() -- Request the fields fbytes = chisel.request_field("evt.rawarg.res") ftime = chisel.request_field("evt.time.s") fisread = chisel.request_field("evt.is_io_read") -- set the filter chisel.set_filter("evt.is_io=true and fd.type=file") chisel.set_interval_s(1) return true end -- Event parsing callback function on_event() bytes = evt.field(fbytes) isread = evt.field(fisread) if bytes ~= nil and bytes > 0 then tot = tot + bytes if isread then totin = totin + bytes else totout = totout + bytes end end return true end function on_interval(delta) etime = evt.field(ftime) print(etime .. " in:" .. totin .. " out:" .. totout .. " tot:" .. tot) tot = 0 totin = 0 totout = 0 return true end sysdig-0.26.4/userspace/sysdig/chisels/iobytes_net.lua000066400000000000000000000032211352731327100230640ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Counts the total bytes read from and written to the network, and prints the result every second"; short_description = "Show total network I/O bytes"; category = "Net"; -- Chisel argument list args = {} tot = 0 totin = 0 totout = 0 -- Initialization callback function on_init() -- Request the fields fbytes = chisel.request_field("evt.rawarg.res") ftime = chisel.request_field("evt.time.s") fisread = chisel.request_field("evt.is_io_read") -- set the filter chisel.set_filter("evt.is_io=true and (fd.type=ipv4 or fd.type=ipv6)") chisel.set_interval_s(1) return true end -- Event parsing callback function on_event() bytes = evt.field(fbytes) isread = evt.field(fisread) if bytes ~= nil and bytes > 0 then tot = tot + bytes if isread then totin = totin + bytes else totout = totout + bytes end end return true end function on_interval(delta) etime = evt.field(ftime) print(etime .. " in:" .. totin .. " out:" .. totout .. " tot:" .. tot) tot = 0 totin = 0 totout = 0 return true end sysdig-0.26.4/userspace/sysdig/chisels/list_login_shells.lua000066400000000000000000000064561352731327100242720ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "List the IDs of the login sessions. Optionally, the list can be filtered to include only the sessions that contain a specific command. The session IDs listed by this chisel can be used as filters for the spy_users chisel. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown."; short_description = "List the login shell IDs"; category = "Security"; -- Chisel argument list args = { { name = "command", description = "If this parameter is specified, only the login shells that contain commands including the given string in their name will be listed. * will match any command name.", argtype = "string", optional = true }, { name = "arguments", description = "If this parameter is specified, only the login shells that contain commands including the given string in their arguments will be listed", argtype = "string", optional = true }, } require "common" sids = { fsid = nil, containername = nil, containerid = nil } -- Argument notification callback function on_set_arg(name, val) if name == "command" then if val ~= "*" then matching_comm_str = val end elseif name == "arguments" then matching_arg_str = val end return true end -- Initialization callback function on_init() fsid = chisel.request_field("proc.loginshellid") fexe = chisel.request_field("evt.arg.exe") fargs = chisel.request_field("evt.arg.args") fcontainername = chisel.request_field("container.name") fcontainerid = chisel.request_field("container.id") -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() chisel.set_filter("evt.type=execve") return true end -- Event parsing callback function on_event() sid = evt.field(fsid) exe = evt.field(fexe) args = evt.field(fargs) containername = evt.field(fcontainername) containerid = evt.field(fcontainerid) if sid and exe then if matching_comm_str and string.find(exe, matching_comm_str) == nil then return true end if matching_arg_str and args and string.find(args, matching_arg_str) == nil then return true end sids.fsid = sid sids.containername = containername sids.containerid = containerid end return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end() if matching_comm_str then print("Shells containing " .. matching_comm_str .. ":") else print("All shells:") end for k, v in pairs(sids) do -- The -pc or -pcontainer options was supplied on the cmd line if print_container then print(sids.fsid .. " " .. sids.containername .. " " .. sids.containerid) else print(sids.fsid) end end end sysdig-0.26.4/userspace/sysdig/chisels/lscontainers.lua000066400000000000000000000050031352731327100232440ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "List the running containers and the metadata"; short_description = "List the running containers"; category = "System State"; -- Argument list args = { { name = "desc", description = "Prints the result set as a data structure", argtype = "string", optional = true } } -- Imports and globals require "common" local dctable = {} local capturing = false local filter = nil local desc = false -- Argument initialization Callback function on_set_arg(name, val) if name == "desc" and val == "desc" then desc = true return true end return false end -- Initialization callback function on_init() return true end -- Event parsing callback function on_event() return true end -- Final chisel initialization function on_capture_start() capturing = true return true end -- Event parsing callback function on_event() sysdig.end_capture() return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end(ts_s, ts_ns, delta) if not capturing then return end local ttable = sysdig.get_container_table(filter) -- Print out the result set as a data structure if ( desc ) then print(st(ttable)) else -- Print out the information in a tabular format local sorted_ttable = pairs_top_by_val(ttable, 0, function(t,a,b) return a < b end) print( extend_string("container.type", 15) .. extend_string("container.image", 16) .. extend_string("container.name", 20 ) .. extend_string("container.id", 13) ) print( extend_string("---------------", 15) .. extend_string("----------------", 16) .. extend_string("--------------------", 20 ) .. extend_string("-------------", 13) ) for key, val in sorted_ttable do print(extend_string(tostring(val.type), 15) .. extend_string(tostring(val.image), 16) .. extend_string(tostring(val.name), 20) .. extend_string(tostring(val.id), 13) ) end end end sysdig-0.26.4/userspace/sysdig/chisels/lsof.lua000066400000000000000000000054251352731327100215130ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "This chisel prints the open file descriptors for every process in the system, with an output that is similar to the one of lsof. Output is at a point in time; adjust this in the filter. It defaults to time of evt.num=0"; short_description = "List (and optionally filter) the open file descriptors."; category = "System State"; -- Argument list args = { { name = "filter", description = "A sysdig-like filter expression that allows restricting the FD list. E.g. 'proc.name=foo and fd.name contains /etc'.", argtype = "filter", optional = true } } -- Argument initialization Callback function on_set_arg(name, val) if name == "filter" then filter = val return true end return false end -- Imports and globals require "common" local dctable = {} local capturing = false local filter = nil local match = false -- Argument notification callback function on_set_arg(name, val) if name == "filter" then filter = val return true end return false end -- Initialization callback function on_init() return true end -- Final chisel initialization function on_capture_start() capturing = true return true end -- Event parsing callback function on_event() sysdig.end_capture() match = true return false end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end() if not capturing then return end if match == false then print("empty capture or no event matching the filter") return end local ttable = sysdig.get_thread_table(filter) local sorted_ttable = pairs_top_by_val(ttable, 0, function(t,a,b) return a < b end) print(extend_string("COMMAND", 20) .. extend_string("PID", 8) .. extend_string("TID", 8) .. extend_string("USER", 8) .. extend_string("FD", 8) .. extend_string("TYPE", 12) .. "NAME") for tid, proc in sorted_ttable do local fdtable = proc.fdtable for fd, fdinfo in pairs(fdtable) do print(extend_string(proc.comm, 20) .. extend_string(tostring(proc.pid), 8) .. extend_string(tostring(tid), 8) .. extend_string(proc.username, 8) .. extend_string(tostring(fd), 8) .. extend_string(tostring(fdinfo.type), 12) .. tostring(fdinfo.name)) end end end sysdig-0.26.4/userspace/sysdig/chisels/memcachelog.lua000066400000000000000000000053611352731327100230130ustar00rootroot00000000000000--[[ Copyright (C) 2015 Donatas Abraitis. This file is part of sysdig. 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. --]] --[[ memcachelog.lua - show most used memcached keys. USAGE: sysdig -c memcachelog eg, sysdig -c memcachelog # show memcached get/set utilization sysdig -c memcachelog get # show memcached only get utilization sysdig -c memcachelog set # show memcached only set utilization sysdig -c memcachelog 'set 1000' # show memcached set utilization which object's size is higher than 1000 By default it will print both methods. --]] -- Chisel description description = "Show a log of memcached commands (get/set)" short_description = "memcached requests log" category = "Application" -- Chisel argument list args = { { name = "method", description = "get/set", optional = true }, { name = "size", description = "object size", optional = true } } -- Helpers -- function split(s, delimiter) result = {}; for match in (s..delimiter):gmatch("(.-)"..delimiter) do table.insert(result, match); end return result; end -- Argument notification callback function on_set_arg(name, val) if name == "method" then opt_method = val return true elseif name == "size" then opt_size = tonumber(val) return true end return false end -- Initialization callback function on_init() util = {} start_time = os.time() sysdig.set_filter("(fd.sport=11211 or proc.name=memcached) and evt.is_io=true") sysdig.set_snaplen(4096) data = chisel.request_field("evt.arg[1]") datetime = chisel.request_field("evt.datetime") return true end -- Event callback function on_event() local data = evt.field(data) local line = split(data, " ") if string.match(line[1], '^[gs]et') ~= nil then local method = line[1] local key = line[2] local size = tonumber(line[5]) or 0 if key ~= nil then if opt_method ~= nil and opt_method ~= method then return true end if opt_method == 'set' and size < opt_size then return true end print(string.format("%s method=%s size=%dB key=%s", evt.field(datetime), method, size, key )) end end return true end sysdig-0.26.4/userspace/sysdig/chisels/netlower.lua000066400000000000000000000111221352731327100223760ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] --[[ netlower.lua - trace network I/O slower than a given threshold. USAGE: sysdig -c netlower min_ms eg, sysdig -c netlower 1000 # show network I/O slower than 1000 ms. sysdig -c netlower "1 disable_colors" # show network I/O slower than 1 ms. w/ no colors sysdig -c netlower 1000 # show network I/O slower than 1000 ms and container output --]] -- Chisel description description = "Trace network I/O slower than a threshold, or all network I/O. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown. (Blue represents a process running within a container, and Green represents a host process)" short_description = "Trace slow network I/0" category = "Performance" -- Chisel argument list args = { { name = "min_ms", description = "Minimum millisecond threshold for showing network I/O", argtype = "int", optional = false }, { name = "disable_color", description = "Set to 'disable_colors' if you want to disable color output", argtype = "string", optional = true }, } require "common" terminal = require "ansiterminal" terminal.enable_color(true) -- Argument notification callback function on_set_arg(name, val) if name == "disable_color" and val == "disable_color" then terminal.enable_color(false) elseif name == "min_ms" then min_ms = parse_numeric_input(val, name) end return true end -- Initialization callback function on_init() -- set the following fields on_event() etype = chisel.request_field("evt.type") dir = chisel.request_field("evt.dir") datetime = chisel.request_field("evt.datetime") fname = chisel.request_field("fd.name") pname = chisel.request_field("proc.name") latency = chisel.request_field("evt.latency") fcontainername = chisel.request_field("container.name") fcontainerid = chisel.request_field("container.id") -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() -- filter for network I/O chisel.set_filter("evt.is_io=true and (fd.type=ipv4 or fd.type=ipv6)") -- The -pc or -pcontainer options was supplied on the cmd line if print_container then print(string.format("%-23.23s %-20.20s %-20.20s %-12.12s %-8s %-12s %s", "evt.datetime", "container.id", "container.name", "proc.name", "evt.type", "LATENCY(ms)", "fd.name")) print(string.format("%-23.23s %-20.20s %-20.20s %-12.12s %-8s %-12s %s", "-----------------------", "------------------------------", "------------------------------", "------------", "--------", "------------", "-----------------------------------------")) else print(string.format("%-23.23s %-12.12s %-8s %-12s %s", "evt.datetime", "proc.name", "evt.type", "LATENCY(ms)", "fd.name")) print(string.format("%-23.23s %-12.12s %-8s %-12s %s", "-----------------------", "------------", "--------", "------------", "-----------------------------------------")) end return true end -- Event callback function on_event() local color = terminal.green lat = evt.field(latency) / 1000000 fn = evt.field(fname) if evt.field(dir) == "<" and lat > min_ms then -- If this is a container modify the output color if evt.field(fcontainername) ~= "host" then color = terminal.blue end -- The -pc or -pcontainer options was supplied on the cmd line if print_container then print(color .. string.format("%-23.23s %-20.20s %-20.20s %-12.12s %-8s %12d %s", evt.field(datetime), evt.field(fcontainerid), evt.field(fcontainername), evt.field(pname), evt.field(etype), lat, fn )) else print(color .. string.format("%-23.23s %-12.12s %-8s %12d %s", evt.field(datetime), evt.field(pname), evt.field(etype), lat, fn )) end end return true end sysdig-0.26.4/userspace/sysdig/chisels/netstat.lua000066400000000000000000000054651352731327100222360ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Print the system network connections, with an output that is similar to the one of netstat. Output is at a point in time; adjust this in the filter. It defaults to time of evt.num=0"; short_description = "List (and optionally filter) network connections."; category = "System State"; -- Argument list args = { { name = "filter", description = "A sysdig-like filter expression that allows restricting the FD list. E.g. 'proc.name=foo and fd.port=80'.", argtype = "filter", optional = true } } -- Argument initialization Callback function on_set_arg(name, val) if name == "filter" then filter = val return true end return false end -- Imports and globals require "common" local dctable = {} local capturing = false local filter = "(fd.type=ipv4 or fd.type=ipv6)" local match = false -- Argument notification callback function on_set_arg(name, val) if name == "filter" then filter = filter .. "and (" .. val .. ")" return true end return false end -- Initialization callback function on_init() return true end -- Final chisel initialization function on_capture_start() capturing = true return true end -- Event parsing callback function on_event() sysdig.end_capture() match = true return false end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end() if not capturing then return end if match == false then print("empty capture or no event matching the filter") return end local ttable = sysdig.get_thread_table(filter) print(extend_string("Proto", 6) .. extend_string("Server Address", 25) .. extend_string("Client Address", 25) .. extend_string("State", 15) .. "TID/PID/Program Name") for tid, proc in pairs(ttable) do local fdtable = proc.fdtable for fd, fdinfo in pairs(fdtable) do local cip = fdinfo.cip local cport = fdinfo.cport local state = "ESTABLISHED" if cip == nil then cip = "0.0.0.0" cport = "*" state = "LISTEN" end print(extend_string(fdinfo.l4proto, 6) .. extend_string(fdinfo.sip .. ":" .. fdinfo.sport, 25) .. extend_string(cip .. ":" .. cport, 25) .. extend_string(state, 15) .. tid .. "/" .. proc.pid .. "/" .. proc.comm ) end end end sysdig-0.26.4/userspace/sysdig/chisels/proc_exec_time.lua000066400000000000000000000105231352731327100235300ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] --[[ USAGE: sysdig -c proc_exec_time eg, sysdig -c proc_exec_time # show processes that have finished sysdig -c proc_exec_time disable_colors" # show processes that have finished w/ no colors sysdig -pc -c proc_exec_time # show processes that have finished and container output --]] -- Chisel description description = "List the processes that have finished running, along with their execution time, and color every line based on the total process run time (Green|Blue below thresholds, Yellow at 3 sec, and Red at 10 sec). This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown. (Blue represents a process running within a container, and Green represents a host process)"; short_description = "Show process execution time"; category = "Performance"; -- Chisel argument list args = { { name = "disable_color", description = "Set to 'disable_colors' if you want to disable color output", argtype = "string", optional = true }, } require "common" terminal = require "ansiterminal" terminal.enable_color(true) local THRESHOLD_YELLOW_NS = 3000000000 local THRESHOLD_RED_NS = 10000000000 -- Argument notification callback function on_set_arg(name, val) if val == "disable_colors" then terminal.enable_color(false) end return true end -- Initialization callback function on_init() -- Request the fields that we need fetype = chisel.request_field("evt.type") fexe = chisel.request_field("proc.name") fargs = chisel.request_field("proc.args") fdtime = chisel.request_field("evt.time.s") fduration = chisel.request_field("proc.duration") fcontainername = chisel.request_field("container.name") fcontainerid = chisel.request_field("container.id") -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() -- set the filter chisel.set_filter("evt.type=procexit") -- The -pc or -pcontainer options was supplied on the cmd line if print_container then print(string.format("%-13.13s %-20.20s %-20.20s %-12.12s %s", "proc.duration", "container.id", "container.name", "proc.name", "proc.args")) print(string.format("%-13.13s %-20.20s %-20.20s %-12.12s %s", "-------------", "--------------------", "--------------------", "------------", "--------------------")) else print(string.format("%-13.13s %-12.12s %s", "proc.duration", "proc.name", "proc.args")) print(string.format("%-13.13s %-12.12s %s", "-------------", "------------", "--------------------")) end return true end -- Event parsing callback function on_event() local dtime = evt.field(fdtime) local duration = evt.field(fduration) if duration ~= nil then local color = terminal.green if duration > THRESHOLD_RED_NS then color = terminal.red elseif duration > THRESHOLD_YELLOW_NS then color = terminal.yellow elseif evt.field(fcontainername) ~= "host" then -- if the data is assocaited with a container change the color to blue unless a threshold is met color = terminal.blue end -- The -pc or -pcontainer options was supplied on the cmd line if print_container then print(color .. string.format("%-13.13s %-20.20s %-20.20s %-12.12s %s", format_time_interval(duration), evt.field(fcontainerid), evt.field(fcontainername), evt.field(fexe), evt.field(fargs))) else print(color .. string.format("%-13.13s %-12.12s %s", format_time_interval(duration), evt.field(fexe), evt.field(fargs))) end end return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end() print(terminal.reset) end sysdig-0.26.4/userspace/sysdig/chisels/ps.lua000066400000000000000000000053521352731327100211710ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "List the running processes, with an output that is similar to the one of ps. Output is at a point in time; adjust this in the filter. It defaults to time of evt.num=0"; short_description = "List (and optionally filter) the machine processes."; category = "System State"; -- Argument list args = { { name = "filter", description = "A sysdig-like filter expression that allows restricting the FD list. For example 'fd.name contains /etc' shows all the processes that have files open under /etc.", argtype = "filter", optional = true } } -- Argument initialization Callback function on_set_arg(name, val) if name == "filter" then filter = val return true end return false end -- Imports and globals require "common" local dctable = {} local capturing = false local filter = nil local match = false -- Argument notification callback function on_set_arg(name, val) if name == "filter" then filter = val return true end return false end -- Initialization callback function on_init() return true end function on_capture_start() capturing = true return true end -- Event parsing callback function on_event() sysdig.end_capture() match = true return false end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end(ts_s, ts_ns, delta) if not capturing then return end if match == false then print("empty capture or no event matching the filter") return end local ttable = sysdig.get_thread_table(filter) local sorted_ttable = pairs_top_by_val(ttable, 0, function(t,a,b) return a < b end) print(extend_string("TID", 8) .. extend_string("PID", 8) .. extend_string("USER", 12) .. extend_string("VIRT", 11) .. extend_string("RES", 11) .. extend_string("FDLIMIT", 10) .. extend_string("CMD", 20)) for tid, proc in sorted_ttable do print(extend_string(tostring(tid), 8) .. extend_string(tostring(proc.pid), 8) .. extend_string(proc.username, 12) .. extend_string(format_bytes(proc.vmsize_kb * 1024), 11) .. extend_string(format_bytes(proc.vmrss_kb * 1024), 11) .. extend_string(tostring(proc.fdlimit), 10) .. proc.comm ) end end sysdig-0.26.4/userspace/sysdig/chisels/scallslower.lua000066400000000000000000000103111352731327100230700ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] --[[ scallslower.lua - trace the syscalls slower than a given threshold. USAGE: sysdig -c scallslower min_ms eg, sysdig -c scallslower 1000 # show syscalls slower than 1000 ms. sysdig -c scallslower "1 disable_colors" # show syscalls slower than 1 ms. w/ no colors sysdig -pc -c scallslower 1000 # show syscalls slower than 1000 ms and container output --]] -- Chisel description description = "Trace syscalls slower than a threshold milliseconds. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown. (Blue represents a process running within a container, and Green represents a host process)"; short_description = "Trace slow syscalls"; category = "Performance"; -- Chisel argument list args = { { name = "min_ms", description = "Minimum milliseconds before which a syscall should complete", argtype = "int", optional = false }, { name = "disable_color", description = "Set to 'disable_colors' if you want to disable color output", argtype = "string", optional = true }, } require "common" terminal = require "ansiterminal" terminal.enable_color(true) -- Argument notification callback function on_set_arg(name, val) if name == "disable_color" and val == "disable_color" then terminal.enable_color(false) elseif name == "min_ms" then min_ms = parse_numeric_input(val, name) end return true end -- Initialization callback function on_init() -- set the following fields on_event() etype = chisel.request_field("evt.type") dir = chisel.request_field("evt.dir") datetime = chisel.request_field("evt.datetime") pname = chisel.request_field("proc.name") latency = chisel.request_field("evt.latency") fcontainername = chisel.request_field("container.name") fcontainerid = chisel.request_field("container.id") -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() -- The -pc or -pcontainer options was supplied on the cmd line if print_container then print(string.format("%-23.23s %-20.20s %-20.20s %-23.23s %-20s %s", "evt.datatime", "container.id", "container.name", "proc.name", "LATENCY(ms)", "evt.type")) print(string.format("%-23.23s %-20.20s %-20.20s %-23.23s %-20s %s", "-----------------------", "--------------------", "--------------------", "-----------------------", "--------------------", "--------------------")) else print(string.format("%-23.23s %-23.23s %-20s %s", "evt.datatime", "proc.name", "LATENCY(ms)", "evt.type")) print(string.format("%-23.23s %-23.23s %-20s %s", "-----------------------", "-----------------------", "--------------------", "--------------------")) end return true end -- Event callback function on_event() local color = terminal.green lat = evt.field(latency) if lat == nil then return end lat = lat / 1000000 if lat > min_ms then if evt.field(fcontainername) ~= "host" then color = terminal.blue end -- The -pc or -pcontainer options was supplied on the cmd line if print_container then print(color .. string.format("%-23.23s %-20.20s %-20.20s %-23.23s %-20s %s", evt.field(datetime), evt.field(fcontainerid), evt.field(fcontainername), evt.field(pname), lat, evt.field(etype))) else print(color .. string.format("%-23.23s %-23.23s %-20s %s", evt.field(datetime), evt.field(pname), lat, evt.field(etype))) end end end sysdig-0.26.4/userspace/sysdig/chisels/shellshock_detect.lua000066400000000000000000000042131352731327100242310ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Reports every attempt to execute bash in a way that exploits the shellshock vulnerability (http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-6271). For every attempt, the chisel reports the time, the name of the process trying to run bash, and its PID."; short_description = "print shellshock attacks"; category = "Security"; args = {} require "common" -- Initialization callback function on_init() -- Request the fields that we need fpname = chisel.request_field("proc.pname") fppid = chisel.request_field("proc.ppid") fpid = chisel.request_field("proc.pid") fenv = chisel.request_field("evt.arg.environment") fetime = chisel.request_field("evt.time") -- set the filter chisel.set_filter("proc.name=bash or proc.name=sh and evt.type=execve") print(extend_string("TIME", 22) .. extend_string("PROCNAME", 22) .. extend_string("PID", 8) .. "FUNCTION") return true end -- Event parsing callback function on_event() local env = evt.field(fenv) local pname = evt.field(fpname) local etime = evt.field(fetime) local ppid = evt.field(fppid) if env ~= nil then if string.find(env, "%(%) ?{.+") then local pid = evt.field(fpid) local env_list = sysdig.get_thread_table(filter)[pid].env for i, v in ipairs(env_list) do if string.find(v, "%(%) ?{.+") then local command = string.match(v, "%(%).+") print(extend_string(etime, 22) .. extend_string(pname, 22) .. extend_string(tostring(ppid), 8) .. command) break end end end end return true end sysdig-0.26.4/userspace/sysdig/chisels/spectrogram.lua000066400000000000000000000111111352731327100230630ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "This console visualization shows the frequency of system call latencies. The Y axis unit is time. By default, a new line is created twice a second, but that can be changed by specifying a different refresh time argument. The X axis shows a range of latencies. Each latency value has a color that can be black (no calls), green (tens of calls/s), yellow (hundreds of calls/s) or red (Thousands of calls/s). In other words, red areas mean that there are many system calls taking the specified time to return. Use this chisel in conjunction with filters to visualize latencies for certain processes, types of I/O activity, file systems, etc." short_description = "Visualize OS latency in real time." category = "CPU Usage" -- Chisel argument list args = { { name = "refresh_time", description = "Chart refresh time in milliseconds", argtype = "int", optional = true }, } require "common" terminal = require "ansiterminal" terminal.enable_color(true) refresh_time = 500000000 refresh_per_sec = 1000000000 / refresh_time frequencies = {} colpalette = {22, 28, 64, 34, 2, 76, 46, 118, 154, 191, 227, 226, 11, 220, 209, 208, 202, 197, 9, 1} charpalette = {" ", "░", "▒", "░"} -- Argument initialization Callback function on_set_arg(name, val) if name == "refresh_time" then refresh_time = parse_numeric_input(val, name) * 1000000 refresh_per_sec = 1000000000 / refresh_time return true end return false end -- Initialization callback function on_init() is_tty = sysdig.is_tty() if not is_tty then print("This chisel only works on ANSI terminals. Aborting.") return false end tinfo = sysdig.get_terminal_info() w = tinfo.width h = tinfo.height chisel.set_filter("evt.dir=<") flatency = chisel.request_field("evt.latency") terminal.hidecursor() return true end -- Final chisel initialization function on_capture_start() chisel.set_interval_ns(refresh_time) return true end -- Event parsing callback function on_event() local latency = evt.field(flatency) if latency == 0 then return true end local llatency = math.log10(latency) if(llatency > 11) then llatency = 11 end local norm_llatency = math.floor(llatency * w / 11) + 1 if frequencies[norm_llatency] == nil then frequencies[norm_llatency] = 1 else frequencies[norm_llatency] = frequencies[norm_llatency] + 1 end return true end -- Calculate colors and character to be used function mkcol(n) local col = math.log10(n * refresh_per_sec + 1) / math.log10(1.6) if col < 1 then col = 1 elseif col > #colpalette then col = #colpalette end local low_col = math.floor(col) local high_col = math.ceil(col) local delta = col - low_col local ch = charpalette[math.floor(1 + delta * #charpalette)] -- If delta is > 75% we use 25% fill and flip fg and bg to fake a 75% filled block if delta > .75 then return colpalette[high_col], colpalette[low_col], ch else return colpalette[low_col], colpalette[high_col], ch end end -- Periodic timeout callback function on_interval(ts_s, ts_ns, delta) terminal.moveup(1) for x = 1, w do local fr = frequencies[x] local fg, bg, ch if fr == nil or fr == 0 then terminal.setfgcol(0) terminal.setbgcol(0) ch = " " else fg, bg, ch = mkcol(fr) terminal.setfgcol(fg) terminal.setbgcol(bg) end io.write(ch) end io.write(terminal.reset .. "\n") local x = 0 while true do if x >= w then break end local curtime = math.floor(x * 11 / w) local prevtime = math.floor((x - 1) * 11 / w) if curtime ~= prevtime then io.write("|") local tstr = format_time_interval(math.pow(10, curtime)) io.write(tstr) x = x + #tstr + 1 else io.write(" ") x = x + 1 end end io.write("\n") frequencies = {} return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end(ts_s, ts_ns, delta) if is_tty then -- Include the last sample on_interval(ts_s, ts_ns, 0) -- reset the terminal print(terminal.reset) terminal.showcursor() end return true end sysdig-0.26.4/userspace/sysdig/chisels/spy_file.lua000066400000000000000000000073151352731327100223620ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "This chisel intercepts all reads and writes to all files. Instead of all files, you can limit interception to one file." short_description = "Echo any read/write made by any process to all files. Optionally, you can provide the name of one file to only intercept reads/writes to that file."; category = "I/O"; -- Argument list args = { { name = "read_or_write", description = "Specify 'R' to capture only read events; 'W' to capture only write events; 'RW' to capture read and write events. By default both read and write events are captured.", argtype = "string", optional = true }, { name = "spy_on_file_name", description = "The name of the file which the chisel should spy on for all read and write activity.", argtype = "string", optional = true } } -- Imports and globals require "common" local spy_file_name = nil local read_or_write = nil local verbose = false -- Argument notification callback function on_set_arg(name, val) if name == "read_or_write" then read_or_write = val return true elseif name == "spy_on_file_name" then spy_file_name = val return true end return false end -- Initialization callback function on_init() local filter -- Request the fields that we need fbuf = chisel.request_field("evt.buffer") fdata = chisel.request_field("evt.arg.data") ffdname = chisel.request_field("fd.name") fisw = chisel.request_field("evt.is_io_write") fpid = chisel.request_field("proc.pid") fpname = chisel.request_field("proc.name") fres = chisel.request_field("evt.rawarg.res") ftid = chisel.request_field("thread.tid") fts = chisel.request_field("evt.time") -- increase the snaplen so we capture more of the conversation sysdig.set_snaplen(2000) -- set the output format to ascii sysdig.set_output_format("ascii") -- set the filter if spy_file_name ~= nil and spy_file_name ~= "" then filter = string.format("(fd.name=%s) and ", spy_file_name) else -- watching terminals risks looping in a live capture filter = "(not fd.name contains /dev/pt and not fd.name contains /dev/tty) and " end if read_or_write == "R" or read_or_write == "r" then filter = string.format("%s%s", filter, "evt.is_io_read=true and ") elseif read_or_write == "W" or read_or_write == "w" then filter = string.format("%s%s", filter, "evt.is_io_write=true and ") else filter = string.format("%s%s", filter, "evt.is_io=true and ") end filter = string.format("%s%s", filter, "fd.type=file and evt.dir=< and evt.failed=false") if verbose then print("filter=" .. filter) end chisel.set_filter(filter) return true end -- Event parsing callback function on_event() -- Extract the event details local data = evt.field(fdata) local fdname = evt.field(ffdname) local is_write = evt.field(fisw) local pid = evt.field(fpid) local pname = evt.field(fpname) local res = evt.field(fres) local ts = evt.field(fts) local read_write -- Render the message to screen if is_write == true then read_write = "W" else read_write = "R" end print(string.format("%s %s(%s) %s %s %s %s", ts, pname, pid, read_write, format_bytes(res), fdname, data)) return true end sysdig-0.26.4/userspace/sysdig/chisels/spy_ip.lua000066400000000000000000000052271352731327100220530ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Shows the network payloads exchanged with an IP end-point. You can combine this chisel with the -x, -X or -A sysdig command line switches to customize the screen output"; short_description = "Show the data exchanged with the given IP address"; category = "Net"; -- Chisel argument list args = { { name = "host_ip", description = "The remote host IP address", argtype = "ipv4" }, { name = "disable_color", description = "Set to 'disable_colors' if you want to disable color output", argtype = "string", optional = true }, } require "common" terminal = require "ansiterminal" terminal.enable_color(true) -- Argument notification callback function on_set_arg(name, val) if name == "host_ip" then addr = val return true elseif name == "disable_color" then if val == "disable_colors" then terminal.enable_color(false) end return true end return false end -- Initialization callback function on_init() -- Request the fields that we need fdata = chisel.request_field("evt.arg.data") fisread = chisel.request_field("evt.is_io_read") fres = chisel.request_field("evt.rawarg.res") -- increase the snaplen so we capture more of the conversation sysdig.set_snaplen(1000) -- set the filter chisel.set_filter("evt.is_io=true and (fd.type=ipv4 or fd.type=ipv6) and fd.ip=" .. addr) return true end DIR_READ = 1 DIR_WRITE = 2 direction = nil -- Event parsing callback function on_event() res = evt.field(fres) data = evt.field(fdata) if res == nil or res <= 0 then return true end if data ~= nil then isread = evt.field(fisread) if isread and direction ~= DIR_READ then infostr = string.format("%s------ Read %s", terminal.red, format_bytes(res)) direction = DIR_READ elseif not isread and direction ~= DIR_WRITE then infostr = string.format("%s------ Read %s", terminal.blue, format_bytes(res)) direction = DIR_WRITE end print(infostr) print(data) end return true end function on_capture_end() print(terminal.reset) end sysdig-0.26.4/userspace/sysdig/chisels/spy_logs.lua000066400000000000000000000150641352731327100224070ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Edit this to change which files are watched by this chisel FILE_FILTER = "(fd.name contains .log or fd.name contains _log or fd.name contains /var/log) and not (fd.name contains .gz or fd.name contains .tgz)" -- Chisel description description = "This chisel intercepts all the writes to files containing '.log' or '_log' in their name, and pretty prints them. You can combine this chisel with filters like 'proc.name=foo' (to restrict the output to a specific process), or 'evt.buffer contains foo' (to show only messages including a specific string). You can also write the events generated around each log entry to file by using the dump_file_name and dump_range_ms arguments. If running from a terminal the line will be colored Green = OK; Yellow = Warn; and Red = Error. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown. ( A Blue colored container.name represents a process running within a container)"; short_description = "Echo any write made by any process to a log file. Optionally, export the events around each log message to file."; category = "Logs"; -- Argument list args = { { name = "dump_file_name", description = "The name of the file where the chisel will write the events related to each syslog entry.", argtype = "string", optional = true }, { name = "dump_range_ms", description = "The time interval to capture *before* and *after* each event, in milliseconds. For example, 500 means that 1 second around each displayed event (.5s before and .5s after) will be saved to . The default value for dump_range_ms is 1000.", argtype = "int", optional = true }, { name = "disable_color", description = "Set to 'disable_colors' if you want to disable color output", argtype = "string", optional = true }, } -- Imports and globals require "common" terminal = require "ansiterminal" terminal.enable_color(true) local do_dump = false local dump_file_name = nil local dump_range_ms = "1000" local entrylist = {} local capturing = false local lastfd = "" local verbose = true -- Argument notification callback function on_set_arg(name, val) if name == "dump_file_name" then do_dump = true dump_file_name = val return true elseif name == "dump_range_ms" then dump_range_ms = val return true elseif name == "disable_color" and val == "disable_color" then terminal.enable_color(false) return true end return false end -- Initialization callback function on_init() -- Request the fields that we need fbuf = chisel.request_field("evt.buffer") ftid = chisel.request_field("thread.tid") fpname = chisel.request_field("proc.name") ffdname = chisel.request_field("fd.name") fcontainername = chisel.request_field("container.name") -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() -- increase the snaplen so we capture more of the conversation sysdig.set_snaplen(2000) -- set the output format to ascii sysdig.set_output_format("ascii") -- set the filter chisel.set_filter("(" .. FILE_FILTER .. ") and evt.is_io_write=true and evt.dir=< and evt.failed=false") -- determine if we're printing our output in a terminal is_tty = sysdig.is_tty() return true end -- Final chisel initialization function on_capture_start() if do_dump then if sysdig.is_live() then print("events export not supported on live captures") return false end end capturing = true return true end -- Event parsing callback function on_event() -- Extract the event details local buf = evt.field(fbuf) local fdname local pname local containername = evt.field(fcontainername) if verbose then fdname = evt.field(ffdname) pname = evt.field(fpname) end local msgs = split(buf, "\n") -- Render the message to screen for i, msg in ipairs(msgs) do if #msg ~= 0 then local infostr if verbose then infostr = pname .. " " .. fdname .. " " else infostr = "" end if is_tty then local color = terminal.green local ls = string.lower(msg) if ls.find(ls, "warn") ~= nil then color = terminal.yellow elseif ls.find(msg, "err") then color = terminal.red end -- Setup the colors for the container option -pc local container = "" if print_container then if containername ~= "host" then -- Make container blue container = string.format("%s %s", terminal.blue, containername ); else -- Make container color (Green, Red, or Yellow) container = string.format("%s %s", color, containername ); end end -- Always make sure anything after container is the color (Green, Red, or Yellow) local infostr_msg = string.format("%s %s%s", color, infostr, msg ); -- The -pc or -pcontainer options was supplied on the cmd line if print_container then infostr = string.format("%s %s %s", color, container, infostr_msg) else infostr = string.format("%s %s%s", color, infostr, msg) end else -- The -pc or -pcontainer options was supplied on the cmd line if print_container then infostr = string.format("%s %s%s", containername, infostr, msg) else infostr = string.format("%s%s", infostr, msg) end end print(infostr) end end if do_dump then local hi, low = evt.get_ts() local tid = evt.field(ftid) table.insert(entrylist, {hi, low, tid}) end return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end() if is_tty then print(terminal.reset) end if do_dump then if capturing then local sn = sysdig.get_evtsource_name() local args = "-F -r" .. sn .. " -w" .. dump_file_name .. " " for i, v in ipairs(entrylist) do if i ~= 1 then args = args .. " or " end args = args .. "(evt.around[" .. ts_to_str(v[1], v[2]) .. "]=" .. dump_range_ms .. " and thread.tid=" .. v[3] .. ")" end print("Writing events for " .. #entrylist .. " log entries") sysdig.run_sysdig(args) end end end sysdig-0.26.4/userspace/sysdig/chisels/spy_port.lua000066400000000000000000000052761352731327100224330ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Shows the network payloads exchanged using a given IP port number. You can combine this chisel with the -x, -X or -A sysdig command line switches to customize the screen output"; short_description = "Show the data exchanged using the given IP port number"; category = "Net"; -- Chisel argument list args = { { name = "host_port", description = "The remote host IP port number", argtype = "int" }, { name = "disable_color", description = "Set to 'disable_colors' if you want to disable color output", argtype = "string", optional = true }, } require "common" terminal = require "ansiterminal" terminal.enable_color(true) -- Argument notification callback function on_set_arg(name, val) if name == "host_port" then port = val return true elseif name == "disable_color" then if val == "disable_colors" then terminal.enable_color(false) end return true end return false end -- Initialization callback function on_init() -- Request the fields that we need fdata = chisel.request_field("evt.arg.data") fisread = chisel.request_field("evt.is_io_read") fres = chisel.request_field("evt.rawarg.res") -- increase the snaplen so we capture more of the conversation sysdig.set_snaplen(1000) -- set the filter chisel.set_filter("evt.is_io=true and (fd.type=ipv4 or fd.type=ipv6) and fd.port=" .. port ) return true end DIR_READ = 1 DIR_WRITE = 2 direction = nil -- Event parsing callback function on_event() res = evt.field(fres) data = evt.field(fdata) if res == nil or res <= 0 then return true end if data ~= nil then isread = evt.field(fisread) if isread and direction ~= DIR_READ then infostr = string.format("%s------ Read %s", terminal.red, format_bytes(res)) direction = DIR_READ elseif not isread and direction ~= DIR_WRITE then infostr = string.format("%s------ Write %s", terminal.blue, format_bytes(res)) direction = DIR_WRITE end print(infostr) print(data) end return true end function on_capture_end() print(terminal.reset) end sysdig-0.26.4/userspace/sysdig/chisels/spy_syslog.lua000066400000000000000000000132661352731327100227650ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Print every message written to syslog by any process. You can combine this chisel with filters like 'proc.name=foo' (to restrict the output to a specific process), or 'syslog.message contains foo' (to show only messages including a specific string). You can also write the events generated around each log entry to file by using the dump_file_name and dump_range_ms arguments."; short_description = "Print every message written to syslog. Optionally, export the events around each syslog message to file."; category = "Logs"; -- Argument list args = { { name = "dump_file_name", description = "The name of the file where the chisel will write the events related to each syslog entry.", argtype = "string", optional = true }, { name = "dump_range_ms", description = "The time interval to capture *before* and *after* each event, in milliseconds. For example, 500 means that 1 second around each displayed event (.5s before and .5s after) will be saved to . The default value for dump_range_ms is 1000.", argtype = "int", optional = true }, { name = "disable_color", description = "Set to 'disable_colors' if you want to disable color output", argtype = "string", optional = true }, } -- Imports and globals require "common" terminal = require "ansiterminal" terminal.enable_color(true) local do_dump = false local dump_file_name = nil local dump_range_ms = "1000" local entrylist = {} local capturing = false -- Argument notification callback function on_set_arg(name, val) if name == "dump_file_name" then do_dump = true dump_file_name = val return true elseif name == "dump_range_ms" then dump_range_ms = val return true elseif name == "disable_color" and val == "disable_color" then terminal.enable_color(false) return true end return false end -- Initialization callback function on_init() -- Request the fields that we need ffac = chisel.request_field("syslog.facility.str") fsev = chisel.request_field("syslog.severity.str") fsevcode = chisel.request_field("syslog.severity") fmsg = chisel.request_field("syslog.message") ftid = chisel.request_field("thread.tid") fpname = chisel.request_field("proc.name") fcontainername = chisel.request_field("container.name") fcontainerid = chisel.request_field("container.id") -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() -- increase the snaplen so we capture more of the conversation sysdig.set_snaplen(1000) -- set the filter chisel.set_filter("fd.name contains /dev/log and evt.is_io_write=true and evt.dir=< and evt.failed=false") is_tty = sysdig.is_tty() return true end -- Final chisel initialization function on_capture_start() if do_dump then if sysdig.is_live() then print("events export not supported on live captures") return false end end capturing = true return true end -- Event parsing callback function on_event() local color = "" -- Extract the event details local fac = evt.field(ffac) local sev = evt.field(fsev) local msg = evt.field(fmsg) local sevcode = evt.field(fsevcode) local tid = evt.field(ftid) local pname = evt.field(fpname) local containername = evt.field(fcontainername) local containerid = evt.field(fcontainerid) -- Render the message to screen if is_tty then local color = terminal.green if sevcode == 4 then color = terminal.yellow elseif sevcode < 4 then color = terminal.red elseif containername ~= "host" then -- If -pc or -pcontainer option change default to blue color = terminal.blue else color = terminal.green end -- The -pc or -pcontainer options was supplied on the cmd line if print_container then infostr = string.format("%s%-20s %-20s %s.%s %s[%u] %s", color, containerid, containername, fac, sev, pname, tid, msg) else infostr = string.format("%s%s.%s %s[%u] %s", color, fac, sev, pname, tid, msg) end else if print_container then infostr = string.format("%-20s %-20s %s.%s %s[%u] %s", fac, containerid, containername, sev, pname, tid, msg) else infostr = string.format("%s.%s %s[%u] %s", fac, sev, pname, tid, msg) end end print(infostr) if do_dump then local hi, low = evt.get_ts() table.insert(entrylist, {hi, low, tid}) end return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end() if is_tty then print(terminal.reset) end if do_dump then if capturing then local sn = sysdig.get_evtsource_name() local args = "-F -r" .. sn .. " -w" .. dump_file_name .. " " for i, v in ipairs(entrylist) do if i ~= 1 then args = args .. " or " end args = args .. "(evt.around[" .. ts_to_str(v[1], v[2]) .. "]=" .. dump_range_ms .. " and thread.tid=" .. v[3] .. ")" end print("Writing events for " .. #entrylist .. " log entries") sysdig.run_sysdig(args) end end end sysdig-0.26.4/userspace/sysdig/chisels/spy_users.lua000066400000000000000000000144151352731327100226030ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Lists every command that users launch interactively (e.g. from bash) and every directory users visit. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown. (Blue represents a process running within a container, and Green represents a host process)"; short_description = "Display interactive user activity"; category = "Security"; -- Chisel argument list args = { { name = "max_depth", description = "the maximum depth to show in the hierarchy of processes", argtype = "int", optional = true }, { name = "disable_color", description = "Set to 'disable_colors' if you want to disable color output", argtype = "string", optional = true }, } require "common" terminal = require "ansiterminal" terminal.enable_color(true) MAX_ANCESTOR_NAVIGATION = 16 max_depth = -1 -- Argument notification callback function on_set_arg(name, val) if name == "max_depth" then max_depth = parse_numeric_input(val, name) elseif name == "disable_color" and val == "disable_color" then terminal.enable_color(false) end return true end -- Initialization callback function on_init() -- Request the fields needed for this chisel fetype = chisel.request_field("evt.type") fexe = chisel.request_field("proc.exe") fargs = chisel.request_field("proc.args") fdir = chisel.request_field("evt.arg.path") fuser = chisel.request_field("user.name") fdtime = chisel.request_field("evt.time.s") fpid = chisel.request_field("proc.pid") fppid = chisel.request_field("proc.ppid") fcontainername = chisel.request_field("container.name") fcontainerid = chisel.request_field("container.id") fanames = {} fapids = {} -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() -- set the filter chisel.set_filter("((evt.type=execve and evt.dir=<) or (evt.type=chdir and evt.dir=< and proc.name contains sh and not proc.name contains sshd)) and evt.failed=false") for j = 0, MAX_ANCESTOR_NAVIGATION do fanames[j] = chisel.request_field("proc.aname[" .. j .. "]") fapids[j] = chisel.request_field("proc.apid[" .. j .. "]") end return true end process_tree = {} -- Event parsing callback function on_event() local color = "" -- If -pc or -pcontainer option change default to green if print_container then color = terminal.green end local user = evt.field(fuser) local dtime = evt.field(fdtime) local pid = evt.field(fpid) local ppid = evt.field(fppid) local ischdir = evt.field(fetype) == "chdir" local containername = evt.field(fcontainername) local containerid = evt.field(fcontainerid) local aname local icorr = 1 if ischdir then ppid = pid table.insert(fanames, 0, 0) table.insert(fapids, 0, 0) icorr = 0 end if user == nil then user = "" end if not process_tree[ppid] then -- No parent pid in the table yet. -- Add one and make sure that there's a shell among the ancestors process_tree[ppid] = {-1} for j = 1, MAX_ANCESTOR_NAVIGATION do aname = evt.field(fanames[j]) if aname == nil then if evt.field(fapids[j]) == nil then -- no shell in the ancestor list, hide this command break end elseif string.len(aname) >= 2 and aname:sub(-2) == "sh" then apid = evt.field(fapids[j]) if process_tree[apid] then process_tree[ppid] = {j - 1, apid} else process_tree[ppid] = {0, apid} end end end end if process_tree[ppid][1] == -1 then -- the parent process has already been detected as NOT having a shell ancestor return true end if not process_tree[pid] then process_tree[pid] = {1 + process_tree[ppid][1], process_tree[ppid][2]} end if ischdir then if max_depth ~= -1 then if process_tree[pid][1] - icorr > max_depth then return true end end -- The -pc or -pcontainer options was supplied on the cmd line if print_container then -- Conatiner will print out as blue if containername ~= "host" then color = terminal.blue end print(color .. extend_string("", 4 * (process_tree[pid][1] - icorr)) .. process_tree[pid][2] .. " " .. dtime .. " " .. user .. "@" .. containername ..") cd " .. evt.field(fdir)) else print(color .. extend_string("", 4 * (process_tree[pid][1] - icorr)) .. process_tree[pid][2] .. " " .. dtime .. " " .. user .. ") cd " .. evt.field(fdir)) end else if max_depth ~= -1 then if process_tree[pid][1] - 1 > max_depth then return true end end -- The -pc or -pcontainer options was supplied on the cmd line if print_container then -- Conatiner will print out as blue if containername ~= "host" then color = terminal.blue end print(color .. extend_string("", 4 * (process_tree[pid][1] - 1)) .. process_tree[pid][2] .. " " .. dtime .. " " .. user .. "@" .. containername ..") " .. evt.field(fexe) .. " " .. evt.field(fargs)) else print(color .. extend_string("", 3 * (process_tree[pid][1] - 1)) .. process_tree[pid][2] .. " " .. dtime .. " " .. user ..") " .. evt.field(fexe) .. " " .. evt.field(fargs)) end -- Tabular format, a future option with potentially a chisel cmd line argument? -- print(color .. string.format("%10.10s %-10.10s %-8.8s %-20.20s %-20.20s %-15.15s %s", -- (process_tree[pid][1] - 1), -- process_tree[pid][2], -- dtime, -- containerid, -- containername, -- user, -- evt.field(fexe) .. " " .. evt.field(fargs))) end return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end() print(terminal.reset) end sysdig-0.26.4/userspace/sysdig/chisels/statsd.lua000066400000000000000000000113601352731327100220450ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Statsd client -- -- For statsd protocol info: https://github.com/b/statsd_spec local math = require "math" local os = require "os" math.randomseed(os.time()) local function send_to_socket(self, string) return sysdig.udp_send(string) end local function make_statsd_message(self, stat, delta, kind, sample_rate) -- Build prefix local prefix = "" if self.namespace ~= nil then prefix = self.namespace.."." end -- Escape the stat name stat = stat:gsub("[:|@]", "_") -- Append the sample rate local rate = "" if sample_rate ~= 1 then rate = "|@"..sample_rate end return prefix..stat..":"..delta.."|"..kind..rate end local function send(self, stat, delta, kind, sample_rate, neg) local packet_size = self.packet_size local msg local stat_type = type(stat) if stat_type == 'table' then sample_rate = delta end sample_rate = sample_rate or 1 if not (sample_rate == 1 or math.random() <= sample_rate) then return end if stat_type == 'table' then local t, size = {}, 0 for s, v in pairs(stat) do if kind == 'c' then if type(s) == 'number' then -- this is array or kyes ( increment{'register', 'register_accept'}) s, v = v, 1 end v = neg and -v or v end msg = make_statsd_message(self, s, v, kind, sample_rate) size = size + #msg if t[1] and (size > packet_size) then local msg = table.concat(t, "\n") local ok, err = self:send_to_socket(msg) if not ok then return nil, err end t, size = {}, 0 end t[#t + 1] = msg end msg = table.concat(t, "\n") else msg = make_statsd_message(self, stat, delta, kind, sample_rate) end return self:send_to_socket(msg) end -- Record an instantaneous measurement. It's different from a counter in that -- the value is calculated by the client rather than the server. local function gauge(self, stat, value, sample_rate) return self:send(stat, value, "g", sample_rate) end local function counter_(self, stat, value, sample_rate, ...) return self:send(stat, value, "c", sample_rate, ...) end -- A counter is a gauge whose value is calculated by the statsd server. The -- client merely gives a delta value by which to change the gauge value. local function counter(self, stat, value, sample_rate) return counter_(self, stat, value, sample_rate) end -- Increment a counter by `value`. local function increment(self, stat, value, sample_rate) return counter_(self, stat, value or 1, sample_rate, false) end -- Decrement a counter by `value`. local function decrement(self, stat, value, sample_rate) value = value or 1 if type(stat) == 'string' then value = -value end return counter_(self, stat, value, sample_rate, true) end -- A timer is a measure of the number of milliseconds elapsed between a start -- and end time, for example the time to complete rendering of a web page for -- a user. local function timer(self, stat, ms) return self:send(stat, ms, "ms") end -- A histogram is a measure of the distribution of timer values over time, -- calculated by the statsd server. Not supported by all statsd implementations. local function histogram(self, stat, value) return self:send(stat, value, "h") end -- A meter measures the rate of events over time, calculated by the Statsd -- server. Not supported by all statsd implementations. local function meter(self, stat, value) return self:send(stat, value, "m") end -- A set counts unique occurrences of events between flushes. Not supported by -- all statsd implementations. local function set(self, stat, value) return self:send(stat, value, "s") end return function(options) options = options or {} local host = options.host or "127.0.0.1" local port = options.port or 8125 local namespace = options.namespace or nil local packet_size = options.packet_size or 508 -- RFC791 sysdig.udp_setpeername(host, port) return { namespace = namespace, udp = udp, packet_size = packet_size, gauge = gauge, counter = counter, increment = increment, decrement = decrement, timer = timer, histogram = histogram, meter = meter, send = send, send_to_socket = send_to_socket } end sysdig-0.26.4/userspace/sysdig/chisels/stderr.lua000066400000000000000000000053241352731327100220510ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Print the standard error of any process on screen. Combine this script with a filter to limit the output to a specific process or pid. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown. (Blue represents a process running within a container, and Green represents a host process)"; short_description = "Print stderr of processes"; category = "I/O"; -- Chisel argument list args = { { name = "disable_color", description = "Set to 'disable_colors' if you want to disable color output", argtype = "string", optional = true }, } terminal = require "ansiterminal" terminal.enable_color(true) -- Argument notification callback function on_set_arg(name, val) if name == "disable_color" and val == "disable_color" then terminal.enable_color(false) return true end return false end -- Initialization callback function on_init() -- Request the fields that we need fbuf = chisel.request_field("evt.rawarg.data") fcontainername = chisel.request_field("container.name") fcontainerid = chisel.request_field("container.id") -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() -- increase the snaplen so we capture more of the conversation sysdig.set_snaplen(2000) -- set the filter chisel.set_filter("fd.num=2 and evt.is_io=true") return true end -- Event parsing callback function on_event() local color = "" -- If -pc or -pcontainer option change default to green if print_container then color = terminal.green end local buf = evt.field(fbuf) local containername = evt.field(fcontainername) local containerid = evt.field(fcontainerid) if buf ~= nil then -- The -pc or -pcontainer options was supplied on the cmd line if print_container then -- Conatiner will print out as blue if containername ~= "host" then color = terminal.blue end print(color .. string.format("%-20.20s %-20.20s %s", containerid, containername, buf )) else print(buf) end end return true end sysdig-0.26.4/userspace/sysdig/chisels/stdin.lua000066400000000000000000000024321352731327100216640ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Print the standard input of any process on screen. Combine this script with a filter to limit the output to a specific process or pid."; short_description = "Print stdin of processes"; category = "I/O"; args = {} -- Initialization callback function on_init() -- Request the fields that we need fbuf = chisel.request_field("evt.rawarg.data") -- increase the snaplen so we capture more of the conversation sysdig.set_snaplen(2000) -- set the filter chisel.set_filter("fd.num=0 and evt.is_io=true") return true end -- Event parsing callback function on_event() buf = evt.field(fbuf) if buf ~= nil then print(buf) end return true end sysdig-0.26.4/userspace/sysdig/chisels/stdout.lua000066400000000000000000000024351352731327100220700ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Print the standard output of any process on screen. Combine this script with a filter to limit the output to a specific process or pid."; short_description = "Print stdout of processes"; category = "I/O"; args = {} -- Initialization callback function on_init() -- Request the fields that we need fbuf = chisel.request_field("evt.rawarg.data") -- increase the snaplen so we capture more of the conversation sysdig.set_snaplen(2000) -- set the filter chisel.set_filter("fd.num=1 and evt.is_io=true") return true end -- Event parsing callback function on_event() buf = evt.field(fbuf) if buf ~= nil then print(buf) end return true end sysdig-0.26.4/userspace/sysdig/chisels/subsecoffset.lua000066400000000000000000000116211352731327100232360ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. Copyright (C) 2015 Brendan Gregg. This file is part of sysdig. 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. --]] -- Chisel description description = "This visualizes the subsecond offset time of system call execution. This allows repetitive patterns to be identified, which are lost when averaging at a one second granularity. The Y axis (vertical) shows the passage of time. The X axis (horizontal) shows the passage of time within whole or fractions of a second. By default, the X axis range is 1000 milliseconds; this can be specified as an argument (try 100). Each bucket of the heat map, or spectrogram, is shown as a colored rectangle. The color shows how many syscalls fell into that time and subsecond offset range. It can be black (no calls), green (tens of calls/s), yellow (hundreds of calls/s) or red (Thousands of calls/s). Use this chisel in conjunction with filters to visualize latencies for certain processes, types of I/O activity, file systems, etc." short_description = "Visualize subsecond offset execution time." category = "CPU Usage" -- Chisel argument list args = { { name = "refresh_time", description = "chart refresh time in milliseconds", argtype = "int", optional = true }, } require "common" terminal = require "ansiterminal" terminal.enable_color(true) refresh_time = 1000 * 1000 * 1000 refresh_per_sec = 1 * 1000 * 1000 * 1000 / refresh_time max_label_len = 0 frequencies = {} colpalette = {22, 28, 64, 34, 2, 76, 46, 118, 154, 191, 227, 226, 11, 220, 209, 208, 202, 197, 9, 1} charpalette = {" ", "░", "▒", "░"} -- Argument initialization function on_set_arg(name, val) if name == "refresh_time" then refresh_time = parse_numeric_input(val, name) * 1 * 1000 * 1000 refresh_per_sec = 1 * 1000 * 1000 * 1000 / refresh_time return true end return false end -- Initialization callback function on_init() is_tty = sysdig.is_tty() if not is_tty then print("This chisel only works on ANSI terminals. Aborting.") return false end tinfo = sysdig.get_terminal_info() w = tinfo.width h = tinfo.height max_label_len = string.len("|" .. (0.9 * refresh_time / 10000000) .. "ms") -- trace syscall entry chisel.set_filter("evt.dir=>") rawtime = chisel.request_field("evt.rawtime") terminal.hidecursor() print("Tracing syscalls... red (hot) == high frequency <-> green == low frequency.\n") return true end -- Final chisel initialization function on_capture_start() chisel.set_interval_ns(refresh_time) return true end -- Event parsing callback function on_event() local subsec = evt.field(rawtime) -- subsec is normalized to terminal column location subsec = math.floor((((subsec * 1000 / refresh_time) % 1000) / 1000) * w) if frequencies[subsec] == nil then frequencies[subsec] = 1 else frequencies[subsec] = frequencies[subsec] + 1 end return true end -- Calculate colors and character to be used function mkcol(n) local col = math.log10(n * refresh_per_sec + 1) / math.log10(1.6) if col < 1 then col = 1 elseif col > #colpalette then col = #colpalette end local low_col = math.floor(col) local high_col = math.ceil(col) local delta = col - low_col local ch = charpalette[math.floor(1 + delta * #charpalette)] -- If delta is > 75% we use 25% fill and flip fg and bg to fake a 75% filled block if delta > .75 then return colpalette[high_col], colpalette[low_col], ch else return colpalette[low_col], colpalette[high_col], ch end end -- Periodic timeout callback function on_interval(ts_s, ts_ns, delta) terminal.moveup(1) for x = 1, w do local fr = frequencies[x] local fg, bg, ch if fr == nil or fr == 0 then terminal.setbgcol(0) terminal.setbgcol(0) ch = " " else fg, bg, ch = mkcol(fr) terminal.setfgcol(fg) terminal.setbgcol(bg) end io.write(ch) end io.write(terminal.reset .. "\n") local x = 0 while true do if x >= w then break end local curtime = math.floor(x * 10 / w) local prevtime = math.floor((x - 1) * 10 / w) if curtime ~= prevtime then if (x <= w - max_label_len) then local tstr = "|" .. (math.floor(10 * x / w) * refresh_time / 10000000) .. "ms" io.write(tstr) x = x + string.len(tstr) else io.write(" ") x = x + 1 end else io.write(" ") x = x + 1 end end io.write("\n") frequencies = {} return true end function on_capture_end(ts_s, ts_ns, delta) if is_tty then print(terminal.reset) terminal.showcursor() end return true end sysdig-0.26.4/userspace/sysdig/chisels/table_generator.lua000066400000000000000000000104641352731327100237040ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Given two filter fields, a key and a value, this chisel creates and renders a table to the screen." short_description = "Filter on a key and value" category = "Filter" hidden = true -- Chisel argument list args = { { name = "keys", description = "comma-separated list of filter fields to use for grouping", argtype = "string" }, { name = "keydescs", description = "comma separated list of human readable descriptions for the key", argtype = "string" }, { name = "value", description = "the value to count for every key", argtype = "string" }, { name = "valuedesc", description = "human readable description for the value", argtype = "string" }, { name = "filter", description = "the filter to apply", argtype = "string" }, { name = "top_number", description = "maximum number of elements to display", argtype = "string" }, { name = "value_units", description = "how to render the values in the result. Can be 'bytes', 'time', 'timepct', or 'none'.", argtype = "string" }, } require "common" terminal = require "ansiterminal" grtable = {} filter = "" islive = false fkeys = {} vizinfo = { key_fld = {}, key_desc = {}, value_fld = "", value_desc = "", value_units = "none", top_number = 0, output_format = "normal" } -- Argument notification callback function on_set_arg(name, val) if name == "keys" then vizinfo.key_fld = split(val, ",") return true elseif name == "keydescs" then vizinfo.key_desc = split(val, ",") return true elseif name == "value" then vizinfo.value_fld = val return true elseif name == "valuedesc" then vizinfo.value_desc = val return true elseif name == "filter" then filter = val return true elseif name == "top_number" then vizinfo.top_number = tonumber(val) return true elseif name == "value_units" then vizinfo.value_units = val return true end return false end -- Initialization callback function on_init() if #vizinfo.key_fld ~= #vizinfo.key_desc then print("error: number of entries in keys different from number entries in keydescs") return false end -- Request the fields we need for i, name in ipairs(vizinfo.key_fld) do fkeys[i] = chisel.request_field(name) end fvalue = chisel.request_field(vizinfo.value_fld) -- set the filter if filter ~= "" then chisel.set_filter(filter) end return true end -- Final chisel initialization function on_capture_start() islive = sysdig.is_live() vizinfo.output_format = sysdig.get_output_format() if islive then chisel.set_interval_s(1) if vizinfo.output_format ~= "json" then terminal.clearscreen() terminal.hidecursor() end end return true end -- Event parsing callback function on_event() local key = nil local kv = nil for i, fld in ipairs(fkeys) do kv = evt.field(fld) if kv == nil then return end if key == nil then key = kv else key = key .. "\001\001" .. evt.field(fld) end end value = evt.field(fvalue) if value ~= nil and value > 0 then entryval = grtable[key] if entryval == nil then grtable[key] = value else grtable[key] = grtable[key] + value end end return true end -- Periodic timeout callback function on_interval(ts_s, ts_ns, delta) if vizinfo.output_format ~= "json" then terminal.clearscreen() terminal.moveto(0, 0) end print_sorted_table(grtable, ts_s, 0, delta, vizinfo) -- Clear the table grtable = {} return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end(ts_s, ts_ns, delta) if islive and vizinfo.output_format ~= "json" then terminal.clearscreen() terminal.moveto(0 ,0) terminal.showcursor() return true end print_sorted_table(grtable, ts_s, 0, delta, vizinfo) return true end sysdig-0.26.4/userspace/sysdig/chisels/topconns.lua000066400000000000000000000033651352731327100224140ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Shows the top network connections in terms of total (in+out) bandwidth. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown."; short_description = "Top network connections by total bytes"; category = "Net"; -- Chisel argument list args = {} -- The number of items to show TOP_NUMBER = 30 -- Argument notification callback function on_set_arg(name, val) return false end -- Initialization callback function on_init() -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() if print_container then chisel.exec("table_generator", "container.name,fd.l4proto,fd.name", "container.name,Proto,Conn", "evt.rawarg.res", "Bytes", "(fd.type=ipv4 or fd.type=ipv6) and evt.is_io=true", "" .. TOP_NUMBER, "bytes") else chisel.exec("table_generator", "fd.l4proto,fd.name", "Proto,Conn", "evt.rawarg.res", "Bytes", "(fd.type=ipv4 or fd.type=ipv6) and evt.is_io=true", "" .. TOP_NUMBER, "bytes") end return true end sysdig-0.26.4/userspace/sysdig/chisels/topcontainers_cpu.lua000066400000000000000000000053741352731327100243120ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Show the top containers defined by the highest CPU utilization." short_description = "Top containers by CPU usage" category = "CPU Usage" -- Chisel argument list args = {} require "common" terminal = require "ansiterminal" grtable = {} islive = false fkeys = {} vizinfo = { key_fld = {"container.name"}, key_desc = {"container.name"}, value_fld = "thread.exectime", value_desc = "CPU%", value_units = "timepct", top_number = 10, output_format = "normal" } -- Initialization callback function on_init() -- Request the fields we need for i, name in ipairs(vizinfo.key_fld) do fkeys[i] = chisel.request_field(name) end -- Request the fields we need fvalue = chisel.request_field(vizinfo.value_fld) fcpu = chisel.request_field("thread.cpu") chisel.set_filter("evt.type=procinfo") return true end -- Final chisel initialization function on_capture_start() islive = sysdig.is_live() vizinfo.output_format = sysdig.get_output_format() if islive then chisel.set_interval_s(1) if vizinfo.output_format ~= "json" then terminal.clearscreen() terminal.hidecursor() end end return true end -- Event parsing callback function on_event() local key = nil local kv = nil for i, fld in ipairs(fkeys) do kv = evt.field(fld) if kv == nil then return end if key == nil then key = kv else key = key .. "\001\001" .. evt.field(fld) end end local cpu = evt.field(fcpu) if grtable[key] == nil then grtable[key] = cpu * 10000000 else grtable[key] = grtable[key] + (cpu * 10000000) end return true end -- Periodic timeout callback function on_interval(ts_s, ts_ns, delta) if vizinfo.output_format ~= "json" then terminal.clearscreen() terminal.moveto(0, 0) end print_sorted_table(grtable, ts_s, 0, delta, vizinfo) -- Clear the table grtable = {} return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end(ts_s, ts_ns, delta) if islive and vizinfo.output_format ~= "json" then terminal.clearscreen() terminal.moveto(0 ,0) terminal.showcursor() return true end print_sorted_table(grtable, ts_s, 0, delta, vizinfo) return true end sysdig-0.26.4/userspace/sysdig/chisels/topcontainers_error.lua000066400000000000000000000021551352731327100246460ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Shows the top container in terms of system call errors." short_description = "Top containers by number of errors" category = "Errors" -- Chisel argument list args = {} -- Argument notification callback function on_set_arg(name, val) return false end -- Initialization callback function on_init() chisel.exec("table_generator", "container.name", "container.name", "evt.count", "#Errors", "evt.failed=true and container.name!=host", "100", "none") return true end sysdig-0.26.4/userspace/sysdig/chisels/topcontainers_file.lua000066400000000000000000000023021352731327100244260ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Shows the top containers in terms of total (in+out) bytes to disk." short_description = "Top containers by R+W disk bytes" category = "I/O" -- Chisel argument list args = {} -- The number of items to show TOP_NUMBER = 10 -- Argument notification callback function on_set_arg(name, val) return false end -- Initialization callback function on_init() chisel.exec("table_generator", "container.name", "container.name", "evt.rawarg.res", "Bytes", "fd.type=file and evt.is_io=true and container.name!=host", "" .. TOP_NUMBER, "bytes") return true end sysdig-0.26.4/userspace/sysdig/chisels/topcontainers_net.lua000066400000000000000000000021011352731327100242720ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Sorted list of containers that use the most network bandwidth." short_description = "Top containers by network I/O" category = "Net" -- Chisel argument list args = {} -- Initialization callback function on_init() chisel.exec("table_generator", "container.name", "container.name", "evt.rawarg.res", "Bytes", "(fd.type=ipv4 or fd.type=ipv6) and evt.is_io=true and container.name!=host", "100", "bytes") return true end sysdig-0.26.4/userspace/sysdig/chisels/topfiles_bytes.lua000066400000000000000000000032101352731327100235710ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Shows the top files in terms of disk usage. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown." short_description = "Top files by R+W bytes" category = "I/O" -- Chisel argument list args = {} -- The number of items to show TOP_NUMBER = 10 -- Argument notification callback function on_set_arg(name, val) return false end -- Initialization callback function on_init() -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() if print_container then chisel.exec("table_generator", "container.name,fd.name", "container.name,Filename", "evt.rawarg.res", "Bytes", "fd.type=file and evt.is_io=true", "" .. TOP_NUMBER, "bytes") else chisel.exec("table_generator", "fd.name", "Filename", "evt.rawarg.res", "Bytes", "fd.type=file and evt.is_io=true", "" .. TOP_NUMBER, "bytes") end return true end sysdig-0.26.4/userspace/sysdig/chisels/topfiles_errors.lua000066400000000000000000000031651352731327100237700ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Shows the top files in terms of I/O errors. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown." short_description = "Top files by number of errors" category = "Errors" -- Chisel argument list args = {} -- The number of items to show TOP_NUMBER = 10 -- Argument notification callback function on_set_arg(name, val) return false end -- Initialization callback function on_init() -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() if print_container then chisel.exec("table_generator", "fd.name,container.name", "Filename,container.name", "evt.count", "#Errors", "fd.type=file and evt.failed=true", "100", "none") else chisel.exec("table_generator", "fd.name", "Filename", "evt.count", "#Errors", "fd.type=file and evt.failed=true", "100", "none") end return true end sysdig-0.26.4/userspace/sysdig/chisels/topfiles_time.lua000066400000000000000000000031711352731327100234070ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Shows the top files in terms of disk usage. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown." short_description = "Top files by time" category = "I/O" -- Chisel argument list args = {} -- The number of items to show TOP_NUMBER = 10 -- Argument notification callback function on_set_arg(name, val) return false end -- Initialization callback function on_init() -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() if print_container then chisel.exec("table_generator", "fd.name,container.name", "Filename,container.name", "evt.latency", "Time", "fd.type=file and evt.is_io=true", "" .. TOP_NUMBER, "time") else chisel.exec("table_generator", "fd.name", "Filename", "evt.latency", "Time", "fd.type=file and evt.is_io=true", "" .. TOP_NUMBER, "time") end return true end sysdig-0.26.4/userspace/sysdig/chisels/topports_server.lua000066400000000000000000000033351352731327100240260ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Shows the top TCP/UDP server ports in terms of total (in+out) bandwidth. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown."; short_description = "Top TCP/UDP server ports by R+W bytes"; category = "Net"; -- Chisel argument list args = {} -- The number of items to show TOP_NUMBER = 100 -- Argument notification callback function on_set_arg(name, val) return false end -- Initialization callback function on_init() -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() if print_container then chisel.exec("table_generator", "fd.sport,container.name", "Srv Port,container.name", "evt.rawarg.res", "Bytes", "(fd.type=ipv4 or fd.type=ipv6) and evt.is_io=true", "" .. TOP_NUMBER, "bytes") else chisel.exec("table_generator", "fd.sport", "Srv Port", "evt.rawarg.res", "Bytes", "(fd.type=ipv4 or fd.type=ipv6) and evt.is_io=true", "" .. TOP_NUMBER, "bytes") end return true end sysdig-0.26.4/userspace/sysdig/chisels/topprocs_cpu.lua000066400000000000000000000064671352731327100232770ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Show the top process defined by the highest CPU utilization. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown." short_description = "Top processes by CPU usage" category = "CPU Usage" -- Chisel argument list args = {} require "common" terminal = require "ansiterminal" grtable = {} islive = false fkeys = {} local print_container = false vizinfo = { key_fld = {"proc.name","proc.pid"}, key_desc = {"Process", "PID"}, value_fld = "thread.exectime", value_desc = "CPU%", value_units = "timepct", top_number = 10, output_format = "normal" } -- Initialization callback function on_init() -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() -- Print container info as well if print_container then -- Modify host pid column name and add container information vizinfo.key_fld = {"proc.name", "proc.pid", "proc.vpid", "container.name"} vizinfo.key_desc = {"Process", "Host_pid", "Container_pid", "container.name"} end -- Request the fields we need for i, name in ipairs(vizinfo.key_fld) do fkeys[i] = chisel.request_field(name) end -- Request the fields we need fvalue = chisel.request_field(vizinfo.value_fld) fcpu = chisel.request_field("thread.cpu") chisel.set_filter("evt.type=procinfo") return true end -- Final chisel initialization function on_capture_start() islive = sysdig.is_live() vizinfo.output_format = sysdig.get_output_format() if islive then chisel.set_interval_s(1) if vizinfo.output_format ~= "json" then terminal.clearscreen() terminal.hidecursor() end end return true end -- Event parsing callback function on_event() local key = nil local kv = nil for i, fld in ipairs(fkeys) do kv = evt.field(fld) if kv == nil then return end if key == nil then key = kv else key = key .. "\001\001" .. evt.field(fld) end end local cpu = evt.field(fcpu) if grtable[key] == nil then grtable[key] = cpu * 10000000 else grtable[key] = grtable[key] + (cpu * 10000000) end return true end -- Periodic timeout callback function on_interval(ts_s, ts_ns, delta) if vizinfo.output_format ~= "json" then terminal.clearscreen() terminal.moveto(0, 0) end print_sorted_table(grtable, ts_s, 0, delta, vizinfo) -- Clear the table grtable = {} return true end -- Called by the engine at the end of the capture (Ctrl-C) function on_capture_end(ts_s, ts_ns, delta) if islive and vizinfo.output_format ~= "json" then terminal.clearscreen() terminal.moveto(0 ,0) terminal.showcursor() return true end print_sorted_table(grtable, ts_s, 0, delta, vizinfo) return true end sysdig-0.26.4/userspace/sysdig/chisels/topprocs_errors.lua000066400000000000000000000031571352731327100240150ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Shows the top processes in terms of system call errors. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown." short_description = "top processes by number of errors" category = "Errors" -- Chisel argument list args = {} -- Argument notification callback function on_set_arg(name, val) return false end -- Initialization callback function on_init() -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() if print_container then chisel.exec("table_generator", "proc.name,proc.pid,thread.vtid,container.name", "Process,Host_pid,Container_pid,container.name", "evt.count", "#Errors", "evt.failed=true", "100", "none") else chisel.exec("table_generator", "proc.name,proc.pid", "Process,PID", "evt.count", "#Errors", "evt.failed=true", "100", "none") end return true end sysdig-0.26.4/userspace/sysdig/chisels/topprocs_file.lua000066400000000000000000000033421352731327100234140ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Shows the top processes in terms of total (in+out) bytes to disk. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown." short_description = "Top processes by R+W disk bytes" category = "I/O" -- Chisel argument list args = {} -- The number of items to show TOP_NUMBER = 10 -- Argument notification callback function on_set_arg(name, val) return false end -- Initialization callback function on_init() -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() if print_container then chisel.exec("table_generator", "proc.name,proc.pid,thread.vtid,container.name", "Process,Host_pid,Container_pid,container.name", "evt.rawarg.res", "Bytes", "fd.type=file and evt.is_io=true", "" .. TOP_NUMBER, "bytes") else chisel.exec("table_generator", "proc.name,proc.pid", "Process,PID", "evt.rawarg.res", "Bytes", "fd.type=file and evt.is_io=true", "" .. TOP_NUMBER, "bytes") end return true end sysdig-0.26.4/userspace/sysdig/chisels/topprocs_net.lua000066400000000000000000000031571352731327100232670ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Sort the list of the processes that use the most network bandwidth. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown." short_description = "Top processes by network I/O" category = "Net" -- Chisel argument list args = {} -- Initialization callback function on_init() -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() if print_container then chisel.exec("table_generator", "proc.name,proc.pid,thread.vtid,container.name", "Process,Host_pid,Container_pid,container.name", "evt.rawarg.res", "Bytes", "(fd.type=ipv4 or fd.type=ipv6) and evt.is_io=true", "100", "bytes") else chisel.exec("table_generator", "proc.name,proc.pid", "Process,PID", "evt.rawarg.res", "Bytes", "(fd.type=ipv4 or fd.type=ipv6) and evt.is_io=true", "100", "bytes") end return true end sysdig-0.26.4/userspace/sysdig/chisels/topscalls.lua000066400000000000000000000033431352731327100225510ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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 number of items to show TOP_NUMBER = 30 -- Chisel description description = "Show the top " .. TOP_NUMBER .. " system calls in terms of number of calls. You can use filters to restrict this to a specific process, thread or file. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown." short_description = "Top system calls by number of calls" category = "Performance" -- Chisel argument list args = {} -- Argument notification callback function on_set_arg(name, val) return false end -- Initialization callback function on_init() -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() if print_container then chisel.exec("table_generator", "evt.type,container.name", "Syscall,container.name", "evt.count", "# Calls", "evt.type!=switch", "" .. TOP_NUMBER, "none") else chisel.exec("table_generator", "evt.type", "Syscall", "evt.count", "# Calls", "evt.type!=switch", "" .. TOP_NUMBER, "none") end return true end sysdig-0.26.4/userspace/sysdig/chisels/topscalls_time.lua000066400000000000000000000032641352731327100235710ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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 number of items to show TOP_NUMBER = 30 -- Chisel description description = "Show the top " .. TOP_NUMBER .. " system calls in terms of time spent in each call. You can use filters to restrict this to a specific process, thread or file. This chisel is compatible with containers using the sysdig -pc or -pcontainer argument, otherwise no container information will be shown." short_description = "Top system calls by time" category = "Performance" -- Chisel argument list args = {} -- Argument notification callback function on_set_arg(name, val) return false end -- Initialization callback function on_init() -- The -pc or -pcontainer options was supplied on the cmd line print_container = sysdig.is_print_container_data() if print_container then chisel.exec("table_generator", "evt.type,container.name", "Syscall,container.name", "evt.latency", "Time", "", "" .. TOP_NUMBER, "time") else chisel.exec("table_generator", "evt.type", "Syscall", "evt.latency", "Time", "", "" .. TOP_NUMBER, "time") end return true end sysdig-0.26.4/userspace/sysdig/chisels/tracers_2_statsd.lua000066400000000000000000000037221352731327100240140ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "Converts sysdig span duration data into statsd metrics and pipes them to the given statsd server. See https://github.com/draios/sysdig/wiki/Tracers for more information."; short_description = "Export spans duration as statds metrics."; category = "Tracers"; args = { { name = "server_addr", description = "The address of the statsd server to send data to", argtype = "string", optional = true }, { name = "server_port", description = "The UDP port to use", argtype = "string", optional = true }, } local lstatsd = require "statsd" local host = "127.0.0.1" local port = 8125 -- Argument notification callback function on_set_arg(name, val) if name == "server_addr" then host = val return true elseif name == "server_port" then port = tonumber(val) return true end return false end -- Initialization callback function on_init() -- Initialize statsd statsd = lstatsd({host = "127.0.0.1"}) -- Request the fields that we need ftags = chisel.request_field("span.tags") flatency = chisel.request_field("span.duration") -- set the filter chisel.set_filter("evt.type=tracer and evt.dir=<") return true end -- Event parsing callback function on_event() local tags = evt.field(ftags) local latency = evt.field(flatency) if latency then statsd:timer(tags, tonumber(latency) / 1000000) end return true end sysdig-0.26.4/userspace/sysdig/chisels/udp_extract.lua000066400000000000000000000042301352731327100230630ustar00rootroot00000000000000--[[ Copyright (C) 2018 Draios inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . --]] local OUTPUT_DIR_NAME = "./udp_dump_files" description = "This chisel parses a trace file, identifies file descriptors carrying UDP network traffic (DNS excluded) and dumps the content of each FD into a different file in the " .. OUTPUT_DIR_NAME .. " directory. Files are named after the UDP tuple they contain."; short_description = "extract data from UDP streams to files."; category = "I/O"; args = {} files = {} function mkdir(dirname) os.execute('mkdir ' .. dirname .. " 2> /dev/null") os.execute('md ' .. dirname .. " 2> nul") end function on_init() fbuf = chisel.request_field("evt.rawarg.data") fres = chisel.request_field("evt.rawarg.res") ffdname = chisel.request_field("fd.name") ffdtype = chisel.request_field("fd.type") mkdir(OUTPUT_DIR_NAME) sysdig.set_snaplen(16384) chisel.set_filter("evt.dir=< and evt.rawres>=0 and fd.l4proto=udp and evt.is_io=true") return true end function on_capture_start() if sysdig.is_live() then print("live capture not supported") return false end return true end function on_event() local buf = evt.field(fbuf) local etype = evt.get_type() local res = evt.field(fres) local fdname = evt.field(ffdname) local fdtype = evt.field(fdtype) local containername = evt.field(fcontainername) local is_io_read = evt.field(fis_io_read) local is_io_write = evt.field(fis_io_write) if not files[fdname] then file_name = OUTPUT_DIR_NAME .. "/" .. fdname file_name = string.gsub(file_name, ":", "_") file_name = string.gsub(file_name, ">", "-") files[fdname] = io.open(file_name, "w") end files[fdname]:write(buf) return true end sysdig-0.26.4/userspace/sysdig/chisels/v_backlog.lua000066400000000000000000000040621352731327100224730ustar00rootroot00000000000000--[[ Copyright (C) 2015 Donatas Abraitis. This file is part of sysdig. 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. --]] view_info = { id = "backlog", name = "Socket Queues", description = "This will show queues (backlog) utilization per process. This is useful if you have no clue what's going to with your system with heavy workload on sockets. It would help you to troubleshoot current listen() backlog, maximum backlog, which is configured by application", tags = {"Default"}, view_type = "table", applies_to = {"", "proc.pid", "proc.name", "fd.sport", "fd.sproto"}, filter = "evt.type=accept", is_root = false, use_defaults = true, drilldown_target = "connections", columns = { { name = "NA", field = "fd.sport", is_key = true }, { name = "PID", field = "proc.pid", description = "Process ID", colsize = 15 }, { name = "PORT", field = "fd.sport", description = "Server port", colsize = 15 }, { name = "BACKLOG", field = "evt.arg[3]", description = "Current backlog size", colsize = 15, is_sorting = true, aggregation = "AVG" }, { name = "BACKLOG_PCT", field = "evt.arg[2]", description = "Current backlog size in percentage", colsize = 15, aggregation = "AVG" }, { name = "BACKLOG_MAX", field = "evt.arg[4]", description = "Max backlog size", colsize = 15 }, { name = "PROC", field = "proc.name", description = "Process name", colsize = 50 }, } } sysdig-0.26.4/userspace/sysdig/chisels/v_connections.lua000066400000000000000000000100131352731327100234040ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "connections", name = "Connections", description = "Top network connections. This view lists all of the network connections that were active during the last sampling interval, with details for each of them.", tips = {"This view can be applied not only to the whole machine, but also to single processes, containers, threads and so on. Use it after a drill down for more fine grained investigation."}, tags = {"Default", "wsysdig"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "fd.dport", "fd.dproto", "fd.port", "fd.proto", "fd.lport", "fd.rport", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, filter = "fd.type=ipv4 or fd.type=ipv6 and fd.name!=''", use_defaults = true, drilldown_target = "incoming_connections", columns = { { tags = {"default"}, name = "NA", field = "fd.name", is_key = true }, { tags = {"containers"}, name = "NA", field = "fd.containername", is_key = true }, { name = "L4PROTO", description = "The connection transport protocol (TCP, UDP, etc.).", field = "fd.l4proto", colsize = 8, }, { name = "LIP", description = "Local IP Address.", field = "fd.lip", colsize = 17, }, { name = "LPORT", description = "Local Port.", field = "fd.lport", colsize = 8, }, { name = "RIP", description = "Remote IP Address.", field = "fd.rip", colsize = 17, }, { name = "RPORT", description = "Remote Port.", field = "fd.rport", colsize = 8, }, { is_sorting = true, name = "BPS IN", field = "evt.buflen.net.in", description = "This connection's input bandwidth in bytes per second.", colsize = 12, aggregation = "TIME_AVG" }, { name = "BPS OUT", field = "evt.buflen.net.out", description = "This connection's output bandwidth in bytes per second.", colsize = 12, aggregation = "TIME_AVG" }, { name = "IOPS", field = "evt.count", description = "Total (read+write) number of calls per second made on this connection by the owning process.", colsize = 12, aggregation = "TIME_AVG" }, { tags = {"containers"}, name = "Container", field = "container.name", description = "Name of the container. What this field contains depends on the containerization technology. For example, for docker this is the content of the 'NAMES' column in 'docker ps'", colsize = 20 }, { name = "Command", description = "The full command line of the process owning the connection's socket.", field = "proc.exeline", aggregation = "MAX", colsize = 0 } }, actions = { { hotkey = "c", command = "tcpdump -niany host %fd.lip and host %fd.rip and port %fd.lport and port %fd.rport", description = "tcpdump connection", }, { hotkey = "l", command = "tcpdump -niany host %fd.lip", description = "tcpdump local IP", }, { hotkey = "n", command = "nslookup %fd.rip", description = "nslookup remote IP", }, { hotkey = "P", command = "ping %fd.rip", description = "ping remote IP", wait_finish = false }, { hotkey = "r", command = "tcpdump -niany host %fd.rip", description = "tcpdump remot IP", }, { hotkey = "t", command = "traceroute %fd.rip", description = "traceroute remot IP", }, }, } sysdig-0.26.4/userspace/sysdig/chisels/v_containers.lua000066400000000000000000000111461352731327100232370ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "containers", name = "Containers", description = "List all the containers running on this machine, and the resources that each of them uses.", tips = {"Select a container and click enter to drill down into it. At that point, you will be able to access several views that will show you the details of the selected container."}, tags = {"wsysdig"}, view_type = "table", applies_to = {"", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, filter = "container.name != host", use_defaults = true, drilldown_target = "procs", columns = { { name = "NA", field = "thread.tid", is_key = true }, { name = "CPU", field = "thread.cpu", description = "Amount of CPU used by the container.", colsize = 8, aggregation = "AVG", groupby_aggregation = "SUM", is_sorting = true }, { name = "PROCS", field = "evt.count.procinfo", description = "Number of processes currently running inside the container.", aggregation = "AVG", groupby_aggregation = "SUM", colsize = 8, }, { name = "THREADS", field = "evt.count.threadinfo", description = "Number of threads currently running inside the container.", aggregation = "AVG", groupby_aggregation = "SUM", colsize = 8 }, { name = "VIRT", field = "thread.vmsize.b", description = "Total virtual memory for the process.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "RES", field = "thread.vmrss.b", description = "Resident non-swapped memory for the process.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "FILE", field = "evt.buflen.file", description = "Total (input+output) file I/O bandwidth generated by the container, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NET", field = "evt.buflen.net", description = "Total (input+output) network bandwidth generated by the container, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NA", field = "container.id", is_groupby_key = true }, { name = "ENGINE", field = "container.type", description = "Container type.", colsize = 8 }, { name = "IMAGE", field = "container.image", description = "Container image name.", colsize = 30 }, { name = "ID", field = "container.id", description = "Container ID. The format of this column depends on the containerization technology. For example, Docker ID are 12 characters hexadecimal digit strings.", colsize = 13 }, { name = "NAME", field = "container.name", description = "Name of the container.", colsize = 0 }, }, actions = { { hotkey = "a", command = "docker attach %container.id", description = "docker attach" }, { hotkey = "b", command = "docker exec -i -t %container.id /bin/bash", description = "bash shell", wait_finish = false }, { hotkey = "f", command = "docker logs -f %container.id", description = "follow logs" }, { hotkey = "h", command = "docker history %container.image", description = "image history" }, { hotkey = "i", command = "docker inspect %container.id", description = "docker inspect" }, { hotkey = "k", command = "docker kill %container.id", description = "docker kill", ask_confirmation = true }, { hotkey = "l", command = "docker logs %container.id", description = "docker logs" }, { hotkey = "s", command = "docker stop %container.id", description = "docker stop" }, { hotkey = "z", command = "docker pause %container.id", description = "docker pause" }, { hotkey = "u", command = "docker unpause %container.id", description = "docker unpause" }, { hotkey = "w", command = "docker wait %container.id", description = "docker wait" }, }, } sysdig-0.26.4/userspace/sysdig/chisels/v_containers_errors.lua000066400000000000000000000114021352731327100246260ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "containers_errors", name = "Containers Errors", description = "This view shows system error counters for each container running on the machine. Errors are grouped into 4 categories: file I/O, network I/O, memory allocation and 'other'.", tips = { "If you click 'enter' on a selection in this chart, you will be able to see the specific errors that the container is generating.", "Digging into a container by clicking on F6 will let you explore the system calls for that specific container and see the full details about what's causing the errors." }, tags = {"Default"}, view_type = "table", applies_to = {"", "container.id", "fd.name", "fd.containername", "fd.sport", "fd.sproto", "evt.type", "fd.directory", "fd.containerdirectory", "fd.containerdirectory", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, drilldown_target = "errors", filter = "container.name != host", use_defaults = true, columns = { { name = "NA", field = "proc.pid", is_key = true }, { name = "ID", field = "container.id", is_groupby_key = true }, { name = "FILE", field = "evt.count.error.file", description = "Number of file I/O errors generated in the container during the sample interval. On trace files, this is the total for the whole file.", colsize = 8, aggregation = "SUM", groupby_aggregation = "SUM" }, { name = "NET", field = "evt.count.error.net", description = "Number of network I/O errors generated in the container during the sample interval. On trace files, this is the total for the whole file.", colsize = 8, aggregation = "SUM", groupby_aggregation = "SUM" }, { name = "MEMORY", field = "evt.count.error.memory", description = "Number of memory allocation/release related errors generated in the container during the sample interval. On trace files, this is the total for the whole file.", colsize = 8, aggregation = "SUM", groupby_aggregation = "SUM" }, { name = "OTHER", field = "evt.count.error.other", description = "Number of errors generated in the container that don't fall in any of the previous categories. E.g. signal or event related errors. On trace files, this is the total for the whole file.", colsize = 8, aggregation = "SUM", groupby_aggregation = "SUM" }, { name = "ENGINE", field = "container.type", description = "Container type.", colsize = 8 }, { name = "ID", field = "container.id", description = "Container ID. The format of this column depends on the containerization technology. For example, Docker ID are 12 characters hexadecimal digit strings.", colsize = 13 }, { name = "CONTAINER", field = "container.name", description = "Name of the container. What this field contains depends on the containerization technology. For example, for docker this is the content of the 'NAMES' column in 'docker ps'", colsize = 0 } }, actions = { { hotkey = "a", command = "docker attach %container.id", description = "docker attach" }, { hotkey = "b", command = "docker exec -i -t %container.id /bin/bash", description = "bash shell", wait_finish = false }, { hotkey = "f", command = "docker logs -f %container.id", description = "follow logs" }, { hotkey = "h", command = "docker history %container.image", description = "image history" }, { hotkey = "i", command = "docker inspect %container.id", description = "docker inspect" }, { hotkey = "k", command = "docker kill %container.id", description = "docker kill", ask_confirmation = true }, { hotkey = "l", command = "docker logs %container.id", description = "docker logs" }, { hotkey = "s", command = "docker stats %container.id", description = "docker stop" }, { hotkey = "z", command = "docker pause %container.id", description = "docker pause" }, { hotkey = "u", command = "docker unpause %container.id", description = "docker unpause" }, { hotkey = "w", command = "docker wait %container.id", description = "docker wait" }, }, } sysdig-0.26.4/userspace/sysdig/chisels/v_cpus.lua000066400000000000000000000033311352731327100220410ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_infoz = { id = "cores", name = "CPUs", description = "This is the typical top/htop process list, showing usage of resources like CPU, memory, disk and network on a by process basis.", tags = {"Default"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, is_root = true, use_defaults = true, drilldown_target = "procs", columns = { { name = "NA", field = "proc.pid", is_key = true }, { name = "NA", field = "evt.cpu", is_groupby_key = true }, { name = "CORE", description = "CPU or Core ID.", field = "evt.cpu", colsize = 8, }, { name = "CPU", field = "proc.cpu", description = "CPU usage.", colsize = 8, aggregation = "AVG", groupby_aggregation = "SUM", is_sorting = true }, { name = "TH", field = "proc.nthreads", description = "Number of threads that the process contains.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 5 } } } sysdig-0.26.4/userspace/sysdig/chisels/v_directories.lua000066400000000000000000000063421352731327100234100ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "directories", name = "Directories", description = "This view lists the directories that were accessed on the file system. The list can be sorted by metrics like the input/output bytes and the IOPS", tips = {"This view can be applied not only to the whole machine, but also to single processes, containers, threads and so on. Use it after a drill down for more fine grained investigation."}, tags = {"Default", "wsysdig"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, filter = "fd.type=file or fd.type=directory and fd.name!=''", use_defaults = true, drilldown_target = "files", columns = { { tags = {"default"}, name = "NA", field = "fd.directory", is_key = true }, { tags = {"containers"}, name = "NA", field = "fd.containerdirectory", is_key = true }, { name = "BYTES IN", field = "evt.buflen.file.in", description = "Amount of bytes read from the file. For live captures, this is the amount during the last sampling interval. For trace files, this is the total amount for the full file.", colsize = 12, aggregation = "SUM" }, { name = "BYTES OUT", field = "evt.buflen.file.out", description = "amount of bytes written to the file. For live captures, this is the amount during the last sampling interval. For trace files, this is the total amount for the full file.", colsize = 12, aggregation = "SUM" }, { is_sorting = true, name = "OPS", field = "evt.count.exit", description = "Number of I/O operations on the file. This counts all the operations on the file, including, open, close, read, write, stat, and so on. As a consequence, this value can be nonzero even if I/O bytes for the file are zero.", colsize = 9, aggregation = "SUM" }, { name = "OPENS", field = "evt.type.is.3", description = "Number times the file has been opened.", colsize = 9, aggregation = "SUM" }, { name = "ERRORS", field = "evt.count.error", description = "Number I/O errors that happened on this file.", colsize = 9, aggregation = "SUM" }, { tags = {"containers"}, name = "Container", field = "container.name", colsize = 20 }, { name = "DIRNAME", description = "The full directory path name.", field = "fd.directory", colsize = 0 } }; actions = { { hotkey = "l", command = "ls -al %fd.directory", description = "ls directory" }, }, } sysdig-0.26.4/userspace/sysdig/chisels/v_docker_events.lua000066400000000000000000000032211352731327100237200ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "docker_events", name = "Docker Events", description = "Lists all the events generated by docker activity, for example container start, stop, kill, etc.", tags = {"Default", "nocsysdig"}, view_type = "list", applies_to = {""}, filter = "evt.type=infra", use_defaults = true, columns = { { name = "TIME", field = "evt.time", description = "Time when the event happened.", colsize = 18, }, { name = "EVENT TYPE", field = "evt.arg.name", description = "Type of docker event.", colsize = 20, }, { name = "CONTAINER ID", field = "evt.infra.docker.container.id", description = "ID of the container that generated this event.", colsize = 20, }, { name = "CONTAINER IMAGE", field = "evt.infra.docker.container.image", description = "Image name of the container that generated this event.", colsize = 20, }, { name = "CONTAINER NAME", field = "evt.infra.docker.container.name", description = "Name of the container that generated this event.", colsize = 0, }, } } sysdig-0.26.4/userspace/sysdig/chisels/v_errors.lua000066400000000000000000000040761352731327100224120ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "errors", name = "Errors", description = "This view shows the top system call errors, sorted by number of occurrences. Errors are shows as errno codes. Do a 'man errno' to find the meaning of the most important codes.", tips = { "This view can be applied not only to the whole machine, but also to single processes, containers, threads and so on. Use it after a drill down for more fine grained investigation.", "Drill down on an error by clicking enter to see which processes are generating it." }, tags = {"Default", "wsysdig"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "fd.directory", "fd.containerdirectory", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, filter = "evt.res != SUCCESS", use_defaults = true, drilldown_target = "procs_errors", columns = { { name = "NA", field = "evt.res", is_key = true }, { name = "COUNT", field = "evt.count", description = "The number of times the error happened during the sample interval. On trace files, this is the total for the whole file.", colsize = 12, aggregation = "SUM", is_sorting = true, }, { name = "ERROR", description = "The error 'errno' code. Do a 'man errno' to find the meaning of the most important codes.", field = "evt.res", colsize = 0 } } } sysdig-0.26.4/userspace/sysdig/chisels/v_file_opens.lua000066400000000000000000000047411352731327100232200ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "file_opens", name = "File Opens List", description = "List file name and process for of every single file open.", tips = {"The RES column is very useful to identify failed opens. Successful opens will show 'SUCCESS' in this column, while failed opens will show an errno code. Do a 'man errno' to find the meaning of the most important codes. And remember that you can sort the opens based on this code, or filter for specific codes using the F4 key."}, tags = {"Default"}, view_type = "list", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "fd.name", "fd.containername", "fd.directory", "fd.containerdirectory", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, filter = "evt.type=open and evt.dir=<", columns = { { name = "TIME", field = "evt.time", description = "The timestamp of the file open.", colsize = 19, }, { name = "RES", field = "evt.res", description = "The result of the open call. This can be either 'SUCCESS', or an errno code.", colsize = 8, }, { name = "FILE", field = "fd.name", description = "The file name.", colsize = 40, }, { tags = {"containers"}, name = "Container", field = "container.name", description = "Name of the container. What this field contains depends on the containerization technology. For example, for docker this is the content of the 'NAMES' column in 'docker ps'", colsize = 20 }, { name = "Command", field = "proc.exeline", aggregation = "MAX", description = "The program that opened the file, including its arguments.", colsize = 0, } }, actions = { { hotkey = "l", command = "less %fd.name", description = "less file", wait_finish = false }, }, } sysdig-0.26.4/userspace/sysdig/chisels/v_files.lua000066400000000000000000000066751352731327100222070ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "files", name = "Files", description = "This view lists the files that were accessed on the file system. The list can be sorted by metrics like the input/output bytes and the IOPS", tips = {"This view can be applied not only to the whole machine, but also to single processes, containers, threads and so on. Use it after a drill down for more fine grained investigation."}, tags = {"Default", "wsysdig"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "fd.directory", "fd.containerdirectory", "fd.containerdirectory", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, filter = "fd.type=file or fd.type=directory and fd.name!=''", use_defaults = true, drilldown_target = "procs", columns = { { tags = {"default"}, name = "NA", field = "fd.name", is_key = true }, { tags = {"containers"}, name = "NA", field = "fd.containername", is_key = true }, { name = "BYTES IN", field = "evt.buflen.file.in", description = "Amount of bytes read from the file. For live captures, this is the amount during the last sampling interval. For trace files, this is the total amount for the full file.", colsize = 12, aggregation = "SUM" }, { name = "BYTES OUT", field = "evt.buflen.file.out", description = "amount of bytes written to the file. For live captures, this is the amount during the last sampling interval. For trace files, this is the total amount for the full file.", colsize = 12, aggregation = "SUM" }, { is_sorting = true, name = "OPS", field = "evt.count.exit", description = "Number of I/O operations on the file. This counts all the operations on the file, including, open, close, read, write, stat, and so on. As a consequence, this value can be nonzero even if I/O bytes for the file are zero.", colsize = 9, aggregation = "SUM" }, { name = "OPENS", field = "evt.type.is.3", description = "Number times the file has been opened during the sample interval. On trace files, this is the total for the whole file.", colsize = 9, aggregation = "SUM" }, { name = "ERRORS", field = "evt.count.error", description = "Number I/O errors that happened on this file during the sample interval. On trace files, this is the total for the whole file.", colsize = 9, aggregation = "SUM" }, { tags = {"containers"}, name = "Container", field = "container.name", colsize = 20 }, { name = "FILENAME", description = "The file name including its full path.", field = "fd.name", colsize = 0 } }, actions = { { hotkey = "l", command = "less %fd.name", description = "less file", wait_finish = false }, }, } sysdig-0.26.4/userspace/sysdig/chisels/v_incoming_connections.lua000066400000000000000000000036001352731327100252730ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "incoming_connections", name = "New Connections", description = "List every newly established network connection.", tags = {"Default"}, view_type = "list", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "thread.tid", "proc.name", "fd.name", "fd.containername", "fd.sport", "fd.sproto", "fd.dport", "fd.port", "fd.lport", "fd.rport", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, filter = "evt.type=accept and evt.dir=< and evt.failed=false", columns = { { name = "TIME", field = "evt.time", description = "Time when the connection was received by this machine.", colsize = 19, }, { name = "Connection", field = "fd.name", description = "Connection tuple details.", colsize = 40, }, { tags = {"containers"}, name = "Container", field = "container.name", description = "Name of the container. This field depends on the containerization technology. For docker this is the 'NAMES' column in 'docker ps'", colsize = 20 }, { name = "Command", field = "proc.exeline", aggregation = "MAX", description = "Name and arguments of the process that received the connection.", colsize = 0 } } } sysdig-0.26.4/userspace/sysdig/chisels/v_io_by_type.lua000066400000000000000000000052041352731327100232320ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "io_by_type", name = "I/O by Type", description = "Show an overview of the I/O volume based on I/O type. Possible I/O types are: file, directory, ipv4 or ipv6 network traffic, pipe, unix socket, signal fd, event fd, inotify fd.", tips = {"This view is a good starting point to understand what a machine is doing besides CPU computation. Remeber that you can apply it to a process or to a container as well, to get an overview of what they are doing."}, tags = {"Default", "wsysdig"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, use_defaults = true, drilldown_target = "procs", columns = { { name = "NA", field = "fd.type", is_key = true }, { name = "BPS IN", field = "evt.buflen.in", description = "Bytes per second read from the FDs of the specific type.", colsize = 12, aggregation = "TIME_AVG" }, { name = "BPS OUT", field = "evt.buflen.out", description = "Bytes per second written to the FDs of the specific type.", colsize = 12, aggregation = "TIME_AVG" }, { is_sorting = true, name = "IOPS", field = "evt.count", description = "Number of I/O operations for the specified I/O category. This counts all the operations on the file, including, open, close, read, write, stat, and so on. As a consequence, this value can be nonzero even if I/O bytes for the file are zero.", colsize = 9, aggregation = "TIME_AVG" }, { name = "TIME", field = "evt.latency", description = "Time spent by processes doing any I/O operation (including wait) of this type.", colsize = 9, aggregation = "SUM" }, { name = "I/O Type", field = "fd.type", description = "Type of I/O. Can be one of: file, directory, ipv4, ipv6, pipe, unix, signal, event, inotify", aggregation = "SUM", colsize = 0 }, } } sysdig-0.26.4/userspace/sysdig/chisels/v_kubernetes_controllers.lua000066400000000000000000000060441352731327100256700ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "kubernetes_controllers", name = "K8s Controllers", description = "List all Kubernetes controllers running on this machine, and the resources that each of them uses.", tips = {"Select a controller and click enter to drill down into it. At that point, you will be able to access several views that will show you the details of the selected controller."}, view_type = "table", applies_to = {"", "evt.res", "k8s.pod.id", "k8s.svc.id", "k8s.ns.id"}, filter = "k8s.rc.id != ''", use_defaults = true, drilldown_target = "kubernetes_pods", columns = { { name = "NA", field = "thread.tid", is_key = true }, { name = "CPU", field = "thread.cpu", description = "Amount of CPU used by the controller.", colsize = 8, aggregation = "AVG", groupby_aggregation = "SUM", is_sorting = true }, { name = "VIRT", field = "thread.vmsize.b", description = "Total virtual memory for the controller.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "RES", field = "thread.vmrss.b", description = "Resident non-swapped memory for the controller.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "FILE", field = "evt.buflen.file", description = "Total (input+output) file I/O bandwidth generated by the controller, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NET", field = "evt.buflen.net", description = "Total (input+output) network bandwidth generated by the controller, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NA", field = "k8s.rc.id", is_groupby_key = true }, { name = "ID", field = "k8s.rc.id", description = "Controller id.", colsize = 38 }, { name = "NAME", field = "k8s.rc.name", description = "Controller name.", colsize = 25 }, { name = "NAMESPACE", field = "k8s.ns.name", description = "Controller namespace.", colsize = 20 }, { name = "LABELS", field = "k8s.rc.labels", description = "Controller labels.", colsize = 0 }, }, actions = { { hotkey = "d", command = "kubectl --namespace=%k8s.ns.name describe rc %k8s.rc.name", description = "describe" }, { hotkey = "x", command = "kubectl --namespace=%k8s.ns.name delete rc %k8s.rc.name", description = "delete" } } } sysdig-0.26.4/userspace/sysdig/chisels/v_kubernetes_deployments.lua000066400000000000000000000061541352731327100256670ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "kubernetes_deployments", name = "K8s Deployments", description = "List all Kubernetes deployments running on this machine, and the resources that each of them uses.", tips = {"Select a deployment and click enter to drill down into it. At that point, you will be able to access several views that will show you the details of the selected deployment."}, view_type = "table", applies_to = {"", "evt.res", "k8s.pod.id", "k8s.svc.id", "k8s.ns.id"}, filter = "k8s.deployment.id != ''", use_defaults = true, drilldown_target = "kubernetes_pods", columns = { { name = "NA", field = "thread.tid", is_key = true }, { name = "CPU", field = "thread.cpu", description = "Amount of CPU used by the deployment.", colsize = 8, aggregation = "AVG", groupby_aggregation = "SUM", is_sorting = true }, { name = "VIRT", field = "thread.vmsize.b", description = "Total virtual memory for the deployment.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "RES", field = "thread.vmrss.b", description = "Resident non-swapped memory for the deployment.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "FILE", field = "evt.buflen.file", description = "Total (input+output) file I/O bandwidth generated by the deployment, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NET", field = "evt.buflen.net", description = "Total (input+output) network bandwidth generated by the deployment, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NA", field = "k8s.deployment.id", is_groupby_key = true }, { name = "ID", field = "k8s.deployment.id", description = "Deployment id.", colsize = 38 }, { name = "NAME", field = "k8s.deployment.name", description = "Deployment name.", colsize = 25 }, { name = "NAMESPACE", field = "k8s.ns.name", description = "Deployment namespace.", colsize = 20 }, { name = "LABELS", field = "k8s.deployment.labels", description = "Deployment labels.", colsize = 0 }, }, actions = { { hotkey = "d", command = "kubectl --namespace=%k8s.ns.name describe deployment %k8s.deployment.name", description = "describe" }, { hotkey = "x", command = "kubectl --namespace=%k8s.ns.name delete deployment %k8s.deployment.name", description = "delete" } } } sysdig-0.26.4/userspace/sysdig/chisels/v_kubernetes_namespaces.lua000066400000000000000000000057001352731327100254370ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "kubernetes_namespaces", name = "K8s Namespaces", description = "List all Kubernetes namespaces running on this machine, and the resources that each of them uses.", tips = {"Select a namespace and click enter to drill down into it. At that point, you will be able to access several views that will show you the details of the selected namespace."}, view_type = "table", applies_to = {"", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id"}, filter = "k8s.ns.id != ''", use_defaults = true, drilldown_target = "kubernetes_pods", columns = { { name = "NA", field = "thread.tid", is_key = true }, { name = "CPU", field = "thread.cpu", description = "Amount of CPU used by the namespace.", colsize = 8, aggregation = "AVG", groupby_aggregation = "SUM", is_sorting = true }, { name = "VIRT", field = "thread.vmsize.b", description = "Total virtual memory for the namespace.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "RES", field = "thread.vmrss.b", description = "Resident non-swapped memory for the namespace.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "FILE", field = "evt.buflen.file", description = "Total (input+output) file I/O bandwidth generated by the namespace, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NET", field = "evt.buflen.net", description = "Total (input+output) network bandwidth generated by the namespace, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NA", field = "k8s.ns.id", is_groupby_key = true }, { name = "ID", field = "k8s.ns.id", description = "Namespace id.", colsize = 38 }, { name = "NAME", field = "k8s.ns.name", description = "Namespace name.", colsize = 25 }, { name = "LABELS", field = "k8s.ns.labels", description = "Namespace labels.", colsize = 0 }, }, actions = { { hotkey = "d", command = "kubectl --namespace=%k8s.ns.name describe namespaces %k8s.ns.name", description = "describe" }, { hotkey = "x", command = "kubectl --namespace=%k8s.ns.name delete namespaces %k8s.ns.name", description = "delete" } } } sysdig-0.26.4/userspace/sysdig/chisels/v_kubernetes_pods.lua000066400000000000000000000067321352731327100242730ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "kubernetes_pods", name = "K8s Pods", description = "List all Kubernetes pods running on this machine, and the resources that each of them uses.", tips = {"Select a pod and click enter to drill down into it. At that point, you will be able to access several views that will show you the details of the selected pod."}, view_type = "table", applies_to = {"", "evt.res", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id"}, filter = "k8s.pod.id != ''", use_defaults = true, drilldown_target = "containers", columns = { { name = "NA", field = "thread.tid", is_key = true }, { name = "CPU", field = "thread.cpu", description = "Amount of CPU used by the pod.", colsize = 8, aggregation = "AVG", groupby_aggregation = "SUM", is_sorting = true }, { name = "VIRT", field = "thread.vmsize.b", description = "Total virtual memory for the pod.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "RES", field = "thread.vmrss.b", description = "Resident non-swapped memory for the pod.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "FILE", field = "evt.buflen.file", description = "Total (input+output) file I/O bandwidth generated by the pod, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NET", field = "evt.buflen.net", description = "Total (input+output) network bandwidth generated by the pod, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NA", field = "k8s.pod.id", is_groupby_key = true }, { name = "ID", field = "k8s.pod.id", description = "Pod name.", colsize = 38 }, { name = "NAME", field = "k8s.pod.name", description = "Pod name.", colsize = 25 }, { name = "NAMESPACE", field = "k8s.ns.name", description = "Pod namespace.", colsize = 20 }, { name = "LABELS", field = "k8s.pod.labels", description = "Pod labels.", colsize = 0 }, }, actions = { { hotkey = "d", command = "kubectl --namespace=%k8s.ns.name describe pods %k8s.pod.name", description = "describe" }, { hotkey = "x", command = "kubectl --namespace=%k8s.ns.name delete pods %k8s.pod.name", description = "delete" }, { hotkey = "b", command = "kubectl --namespace=%k8s.ns.name exec %k8s.pod.name -i -t -- /bin/bash", description = "bash shell" }, { hotkey = "l", command = "kubectl --namespace=%k8s.ns.name logs %k8s.pod.name", description = "log" }, { hotkey = "f", command = "kubectl --namespace=%k8s.ns.name logs -f %k8s.pod.name", description = "follow log" }, { hotkey = "o", command = "kubectl --namespace=%k8s.ns.name logs -p %k8s.pod.name", description = "previous log" } } } sysdig-0.26.4/userspace/sysdig/chisels/v_kubernetes_replicasets.lua000066400000000000000000000060531352731327100256400ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "kubernetes_replicasets", name = "K8s ReplicaSets", description = "List all Kubernetes replica sets running on this machine, and the resources that each of them uses.", tips = {"Select a replica set and click enter to drill down into it. At that point, you will be able to access several views that will show you the details of the selected controller."}, view_type = "table", applies_to = {"", "evt.res", "k8s.pod.id", "k8s.svc.id", "k8s.ns.id"}, filter = "k8s.rs.id != ''", use_defaults = true, drilldown_target = "kubernetes_pods", columns = { { name = "NA", field = "thread.tid", is_key = true }, { name = "CPU", field = "thread.cpu", description = "Amount of CPU used by the replica set.", colsize = 8, aggregation = "AVG", groupby_aggregation = "SUM", is_sorting = true }, { name = "VIRT", field = "thread.vmsize.b", description = "Total virtual memory for the replica set.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "RES", field = "thread.vmrss.b", description = "Resident non-swapped memory for the replica set.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "FILE", field = "evt.buflen.file", description = "Total (input+output) file I/O bandwidth generated by the replica set, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NET", field = "evt.buflen.net", description = "Total (input+output) network bandwidth generated by the replica set, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NA", field = "k8s.rs.id", is_groupby_key = true }, { name = "ID", field = "k8s.rs.id", description = "Controller id.", colsize = 38 }, { name = "NAME", field = "k8s.rs.name", description = "Controller name.", colsize = 25 }, { name = "NAMESPACE", field = "k8s.ns.name", description = "Controller namespace.", colsize = 20 }, { name = "LABELS", field = "k8s.rs.labels", description = "Controller labels.", colsize = 0 }, }, actions = { { hotkey = "d", command = "kubectl --namespace=%k8s.ns.name describe rs %k8s.rs.name", description = "describe" }, { hotkey = "x", command = "kubectl --namespace=%k8s.ns.name delete rs %k8s.rs.name", description = "delete" } } } sysdig-0.26.4/userspace/sysdig/chisels/v_kubernetes_services.lua000066400000000000000000000060311352731327100251410ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "kubernetes_services", name = "K8s Services", description = "List all Kubernetes services running on this machine, and the resources that each of them uses.", tips = {"Select a service and click enter to drill down into it. At that point, you will be able to access several views that will show you the details of the selected service."}, view_type = "table", applies_to = {"", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.ns.id"}, filter = "k8s.svc.id != ''", use_defaults = true, drilldown_target = "kubernetes_pods", columns = { { name = "NA", field = "thread.tid", is_key = true }, { name = "CPU", field = "thread.cpu", description = "Amount of CPU used by the service.", colsize = 8, aggregation = "AVG", groupby_aggregation = "SUM", is_sorting = true }, { name = "VIRT", field = "thread.vmsize.b", description = "Total virtual memory for the service.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "RES", field = "thread.vmrss.b", description = "Resident non-swapped memory for the service.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "FILE", field = "evt.buflen.file", description = "Total (input+output) file I/O bandwidth generated by the service, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NET", field = "evt.buflen.net", description = "Total (input+output) network bandwidth generated by the service, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NA", field = "k8s.svc.id", is_groupby_key = true }, { name = "ID", field = "k8s.svc.id", description = "Service id.", colsize = 38 }, { name = "NAME", field = "k8s.svc.name", description = "Service name.", colsize = 25 }, { name = "NAMESPACE", field = "k8s.ns.name", description = "Service namespace.", colsize = 20 }, { name = "LABELS", field = "k8s.svc.labels", description = "Service labels.", colsize = 0 }, }, actions = { { hotkey = "d", command = "kubectl --namespace=%k8s.ns.name describe services/%k8s.svc.name", description = "describe" }, { hotkey = "x", command = "kubectl --namespace=%k8s.ns.name delete services %k8s.svc.name", description = "delete" } } } sysdig-0.26.4/userspace/sysdig/chisels/v_marathon_apps.lua000066400000000000000000000047501352731327100237310ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "marathon_apps", name = "Marathon Apps", description = "List all Marathon apps running on this machine, and the resources that each of them uses.", tips = {"Select an app and click enter to drill down into it. At that point, you will be able to access several views that will show you the details of the selected app."}, view_type = "table", applies_to = {"", "evt.res", "marathon.group.id"}, filter = "marathon.app.id != ''", use_defaults = true, drilldown_target = "mesos_tasks", columns = { { name = "NA", field = "thread.tid", is_key = true }, { name = "CPU", field = "thread.cpu", description = "Amount of CPU used by the app.", colsize = 8, aggregation = "AVG", groupby_aggregation = "SUM", is_sorting = true }, { name = "VIRT", field = "thread.vmsize.b", description = "Total virtual memory for the app.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "RES", field = "thread.vmrss.b", description = "Resident non-swapped memory for the app.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "FILE", field = "evt.buflen.file", description = "Total (input+output) file I/O bandwidth generated by the app, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NET", field = "evt.buflen.net", description = "Total (input+output) network bandwidth generated by the app, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NA", field = "marathon.app.id", is_groupby_key = true }, { name = "ID", field = "marathon.app.id", description = "App name.", colsize = 38 }, { name = "LABELS", field = "marathon.app.labels", description = "App labels.", colsize = 0 }, } } sysdig-0.26.4/userspace/sysdig/chisels/v_marathon_groups.lua000066400000000000000000000046501352731327100243040ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "marathon_groups", name = "Marathon Groups", description = "List all Marathon Groups running on this machine, and the resources that each of them uses.", tips = {"Select a group and click enter to drill down into it. At that point, you will be able to access several views that will show you the details of the selected group."}, view_type = "table", applies_to = {"", "evt.res", "marathon.app.id"}, filter = "marathon.group.id != ''", use_defaults = true, drilldown_target = "marathon_apps", columns = { { name = "NA", field = "thread.tid", is_key = true }, { name = "CPU", field = "thread.cpu", description = "Amount of CPU used by the framework.", colsize = 8, aggregation = "AVG", groupby_aggregation = "SUM", is_sorting = true }, { name = "VIRT", field = "thread.vmsize.b", description = "Total virtual memory for the framework.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "RES", field = "thread.vmrss.b", description = "Resident non-swapped memory for the framework.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "FILE", field = "evt.buflen.file", description = "Total (input+output) file I/O bandwidth generated by the framework, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NET", field = "evt.buflen.net", description = "Total (input+output) network bandwidth generated by the framework, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NA", field = "marathon.group.name", is_groupby_key = true }, { name = "ID", field = "marathon.group.id", description = "Group id.", colsize = 38 } } } sysdig-0.26.4/userspace/sysdig/chisels/v_mesos_frameworks.lua000066400000000000000000000050501352731327100244550ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "mesos_frameworks", name = "Mesos Frameworks", description = "List all Mesos frameworks running on this machine, and the resources that each of them uses.", tips = {"Select a framework and click enter to drill down into it. At that point, you will be able to access several views that will show you the details of the selected framework."}, view_type = "table", applies_to = {"", "evt.res", "mesos.task.id"}, filter = "mesos.framework.id != ''", use_defaults = true, drilldown_target = "mesos_tasks", columns = { { name = "NA", field = "thread.tid", is_key = true }, { name = "CPU", field = "thread.cpu", description = "Amount of CPU used by the framework.", colsize = 8, aggregation = "AVG", groupby_aggregation = "SUM", is_sorting = true }, { name = "VIRT", field = "thread.vmsize.b", description = "Total virtual memory for the framework.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "RES", field = "thread.vmrss.b", description = "Resident non-swapped memory for the framework.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "FILE", field = "evt.buflen.file", description = "Total (input+output) file I/O bandwidth generated by the framework, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NET", field = "evt.buflen.net", description = "Total (input+output) network bandwidth generated by the framework, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NA", field = "mesos.framework.name", is_groupby_key = true }, { name = "ID", field = "mesos.framework.id", description = "Framework ID.", colsize = 38 }, { name = "NAME", field = "mesos.framework.name", description = "Framework name.", colsize = 25 } } } sysdig-0.26.4/userspace/sysdig/chisels/v_mesos_tasks.lua000066400000000000000000000051121352731327100234210ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "mesos_tasks", name = "Mesos Tasks", description = "List all Mesos tasks running on this machine, and the resources that each of them uses.", tips = {"Select a task and click enter to drill down into it. At that point, you will be able to access several views that will show you the details of the selected task."}, view_type = "table", applies_to = {"", "evt.res", "mesos.framework.id"}, filter = "mesos.task.id != ''", use_defaults = true, drilldown_target = "containers", columns = { { name = "NA", field = "thread.tid", is_key = true }, { name = "CPU", field = "thread.cpu", description = "Amount of CPU used by the task.", colsize = 8, aggregation = "AVG", groupby_aggregation = "SUM", is_sorting = true }, { name = "VIRT", field = "thread.vmsize.b", description = "Total virtual memory for the task.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "RES", field = "thread.vmrss.b", description = "Resident non-swapped memory for the task.", aggregation = "MAX", groupby_aggregation = "SUM", colsize = 9 }, { name = "FILE", field = "evt.buflen.file", description = "Total (input+output) file I/O bandwidth generated by the task, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NET", field = "evt.buflen.net", description = "Total (input+output) network bandwidth generated by the task, in bytes per second.", colsize = 8, aggregation = "TIME_AVG", groupby_aggregation = "SUM" }, { name = "NA", field = "mesos.task.id", is_groupby_key = true }, { name = "ID", field = "mesos.task.id", description = "Task name.", colsize = 38 }, { name = "NAME", field = "mesos.task.name", description = "Task name.", colsize = 25 }, { name = "LABELS", field = "mesos.task.labels", description = "Task labels.", colsize = 0 }, } } sysdig-0.26.4/userspace/sysdig/chisels/v_notifications.lua000066400000000000000000000027661352731327100237530ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "notifications", name = "Notifications", description = "Lists the notification events that indicate the specific point in time when sysdig secure policies have been violated.", tags = {"nocsysdig"}, view_type = "list", applies_to = {""}, filter = "evt.type=notification", use_defaults = true, columns = { { name = "TIME", field = "evt.time", description = "Time when the command was executed.", colsize = 12, }, { name = "ID", field = "evt.arg.id", description = "Notification ID. This can be used to locate the notification in the sysdig secure user interface.", colsize = 24, }, { tags = {"containers"}, name = "Container", field = "container.name", colsize = 20 }, { name = "DESCRIPTION", field = "evt.arg.desc", description = "The description of the policy that generated this notification.", colsize = 0, } } } sysdig-0.26.4/userspace/sysdig/chisels/v_page_faults.lua000066400000000000000000000054061352731327100233660ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "page_faults", name = "Page Faults", description = "This view shows page fault counters for processes. Both minor and major page faults are reported for each process. The counters report the number of page faults since process start.", tips = { "Major page faults are typically the ones you really want to keep an eye on. They are the ones causing pages swapping to disk, thus dramatically slowing down process execution.", "When applying this view on a live system, if the system is well tuned for performance you should see no changes in the first column." }, tags = {"Default", "wsysdig"}, view_type = "table", filter = "evt.type!=switch", applies_to = {"", "container.id", "fd.name", "fd.containername", "fd.sport", "fd.sproto", "evt.type", "fd.directory", "fd.containerdirectory", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, drilldown_target = "errors", use_defaults = true, columns = { { name = "NA", field = "proc.pid", is_key = true }, { name = "MAJOR", field = "thread.pfmajor", description = "Number of major page faults that the process generated since its start.", colsize = 9, aggregation = "MAX", is_sorting = true }, { name = "MINOR", field = "thread.pfminor", description = "Number of minor page faults that the process generated since its start.", colsize = 9, aggregation = "MAX" }, { name = "PID", description = "Process PID.", field = "proc.pid", colsize = 8, }, { tags = {"containers"}, name = "VPID", field = "proc.vpid", description = "PID that the process has inside the container.", colsize = 8, }, { tags = {"containers"}, name = "Container", field = "container.name", description = "Name of the container. What this field contains depends on the containerization technology. For example, for docker this is the content of the 'NAMES' column in 'docker ps'", colsize = 20 }, { name = "Command", description = "Full command line of the process.", field = "proc.exeline", aggregation = "MAX", colsize = 0 } } } sysdig-0.26.4/userspace/sysdig/chisels/v_port_bindings.lua000066400000000000000000000032141352731327100237300ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "port_bindings", name = "Port bindings", description = "Lists the creation (bind) and removal (close) of listening ports on the system.", tags = {"default", "wsysdig", "nocsysdig"}, view_type = "list", applies_to = {""}, filter = "(evt.type=bind and evt.dir=< and (fd.type=ipv4 or fd.type=ipv6)) or (evt.type=close and evt.dir=> and fd.typechar=2 and fd.type=ipv4)", use_defaults = true, columns = { { name = "TIME", field = "evt.time", description = "Time when the action happened.", colsize = 12, }, { name = "OPERATION", field = "evt.type", description = "Action type. Can Be 'bind' (when a new listening port is added) or 'close' (when a listening port is removed).", colsize = 24, }, { name = "PORT", field = "fd.sport", description = "The number of the created/removed port.", colsize = 24, }, { name = "Command", description = "The full command line of the process adding/removing the port.", field = "proc.exeline", aggregation = "MAX", colsize = 0 } } } sysdig-0.26.4/userspace/sysdig/chisels/v_procs.lua000066400000000000000000000107421352731327100222210ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "procs", name = "Processes", description = "This is the typical top/htop process list, showing usage of resources like CPU, memory, disk and network on a by process basis.", tips = {"This is a perfect view to start a drill down session. Click enter or double click on a process to dive into it and explore its behavior."}, tags = {"Default", "wsysdig"}, view_type = "table", filter = "evt.type!=switch", applies_to = {"", "container.id", "fd.name", "fd.containername", "fd.sport", "fd.sproto", "evt.type", "fd.directory", "fd.containerdirectory", "fd.type", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, is_root = true, drilldown_target = "threads", use_defaults = true, columns = { { name = "NA", field = "thread.tid", is_key = true }, { name = "NA", field = "proc.pid", is_groupby_key = true }, { name = "PID", description = "Process PID.", field = "proc.pid", colsize = 7, }, { tags = {"containers"}, name = "VPID", field = "proc.vpid", description = "PID that the process has inside the container.", colsize = 8, }, { name = "CPU", field = "thread.cpu", description = "Amount of CPU used by the proccess.", aggregation = "AVG", groupby_aggregation = "SUM", colsize = 8, is_sorting = true }, { name = "USER", field = "user.name", colsize = 12 }, { name = "TH", field = "proc.nthreads", description = "Number of threads that the process contains.", aggregation = "MAX", groupby_aggregation = "MAX", colsize = 5 }, { name = "VIRT", field = "thread.vmsize.b", description = "Total virtual memory for the process.", aggregation = "MAX", groupby_aggregation = "MAX", colsize = 9 }, { name = "RES", field = "thread.vmrss.b", description = "Resident non-swapped memory for the process.", aggregation = "MAX", groupby_aggregation = "MAX", colsize = 9 }, { name = "FILE", field = "evt.buflen.file", description = "Total (input+output) file I/O bandwidth generated by the process, in bytes per second.", aggregation = "TIME_AVG", groupby_aggregation = "SUM", colsize = 8 }, { name = "NET", field = "evt.buflen.net", description = "Total (input+output) network I/O bandwidth generated by the process, in bytes per second.", aggregation = "TIME_AVG", groupby_aggregation = "SUM", colsize = 8 }, { tags = {"containers"}, name = "CONTAINER", field = "container.name", colsize = 20 }, { name = "Command", description = "The full command line of the process.", field = "proc.exeline", aggregation = "MAX", colsize = 0 } }, actions = { { hotkey = "9", command = "kill -9 %proc.pid", description = "kill -9", ask_confirmation = true, wait_finish = false }, { hotkey = "c", command = "gcore %proc.pid", description = "generate core", }, { hotkey = "g", command = "gdb -p %proc.pid", description = "gdb attach", wait_finish = false }, { hotkey = "k", command = "kill %proc.pid", description = "kill", ask_confirmation = true, wait_finish = false }, { hotkey = "l", command = "ltrace -p %proc.pid", description = "ltrace", }, { hotkey = "s", command = "gdb -p %proc.pid --batch --quiet -ex \"thread apply all bt full\" -ex \"quit\"", description = "print stack", }, { hotkey = "f", command = "lsof -p %proc.pid", description = "one-time lsof", }, { hotkey = "[", command = "renice $(expr $(ps -h -p %proc.pid -o nice) + 1) -p %proc.pid", description = "increment nice by 1", wait_finish = false, }, { hotkey = "]", command = "renice $(expr $(ps -h -p %proc.pid -o nice) - 1) -p %proc.pid", description = "decrement nice by 1", wait_finish = false, }, }, } sysdig-0.26.4/userspace/sysdig/chisels/v_procs_cpu.lua000066400000000000000000000073401352731327100230700ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "procs_cpu", name = "Processes CPU", description = "Show total versus user versus system CPU usage for every process.", tips = { "A high value for both SYS and SYSCALLS likely means that the process is I/O bound. A high value for SYS and a moderate value for SYSCALLS might on the other side indicate a kernel bottleneck. In both cases, drilling down with the 'System Calls' view can help understand what's happening." }, tags = {"Default", "wsysdig"}, view_type = "table", filter = "evt.type!=switch", applies_to = {"", "container.id", "fd.name", "fd.containername", "fd.sport", "fd.sproto", "evt.type", "fd.directory", "fd.containerdirectory", "fd.type", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, is_root = true, drilldown_target = "threads", use_defaults = true, columns = { { name = "NA", field = "thread.tid", is_key = true }, { name = "NA", field = "proc.pid", is_groupby_key = true }, { name = "PID", description = "Process PID.", field = "proc.pid", colsize = 8, }, { tags = {"containers"}, name = "VPID", field = "proc.vpid", description = "PID that the process has inside the container.", colsize = 8, }, { name = "TOT", field = "thread.cpu", description = "Total amount of CPU used by the proccess (user + system).", aggregation = "AVG", groupby_aggregation = "SUM", colsize = 8, is_sorting = true }, { name = "USER", field = "thread.cpu.user", description = "Amount of user CPU used by the proccess.", aggregation = "AVG", groupby_aggregation = "SUM", colsize = 8, }, { name = "SYS", field = "thread.cpu.system", description = "Amount of system CPU used by the proccess.", aggregation = "AVG", groupby_aggregation = "SUM", colsize = 8, }, { name = "SYSCALLS", field = "evt.count", description = "Number of system calls per second made by the proccess.", aggregation = "TIME_AVG", groupby_aggregation = "SUM", colsize = 9, }, { tags = {"containers"}, name = "CONTAINER", description = "The container this process belongs to.", field = "container.name", colsize = 20 }, { name = "Command", description = "The full command line of the process.", field = "proc.exeline", aggregation = "MAX", colsize = 0 } }, actions = { { hotkey = "9", command = "kill -9 %proc.pid", description = "kill -9", ask_confirmation = true, wait_finish = false }, { hotkey = "c", command = "gcore %proc.pid", description = "generate core", }, { hotkey = "g", command = "gdb -p %proc.pid", description = "gdb attach", wait_finish = false }, { hotkey = "k", command = "kill %proc.pid", description = "kill", ask_confirmation = true, wait_finish = false }, { hotkey = "l", command = "ltrace -p %proc.pid", description = "ltrace", }, { hotkey = "s", command = "gdb -p %proc.pid --batch --quiet -ex \"thread apply all bt full\" -ex \"quit\"", description = "print stack", }, }, } sysdig-0.26.4/userspace/sysdig/chisels/v_procs_errors.lua000066400000000000000000000077241352731327100236230ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "procs_errors", name = "Processes Errors", description = "This view shows system error counters for processes. Errors are grouped into 4 categories: file I/O, network I/O, memory allocation and 'other'.", tips = { "If you click 'enter' on a selection in this chart, you will be able to see the specific errors that the process is generating.", "Digging into a process by clicking on F6 will let you explore the system calls for that specific process and see the full details about what's causing the errors." }, tags = {"Default", "wsysdig"}, filter = "evt.type!=switch", view_type = "table", applies_to = {"", "container.id", "fd.name", "fd.containername", "fd.sport", "fd.sproto", "evt.type", "fd.directory", "fd.containerdirectory", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, drilldown_target = "errors", use_defaults = true, columns = { { name = "NA", field = "proc.pid", is_key = true }, { name = "FILE", field = "evt.count.error.file", description = "Number of file I/O errors generated by the process during the sample interval. On trace files, this is the total for the whole file.", colsize = 8, aggregation = "SUM" }, { name = "NET", field = "evt.count.error.net", description = "Number of network I/O errors generated by the process during the sample interval. On trace files, this is the total for the whole file.", colsize = 8, aggregation = "SUM" }, { name = "MEMORY", field = "evt.count.error.memory", description = "Number of memory allocation/release related errors generated by the process during the sample interval. On trace files, this is the total for the whole file.", colsize = 8, aggregation = "SUM" }, { name = "OTHER", field = "evt.count.error.other", description = "Number of errors generated by the process that don't fall in any of the previous categories. E.g. signal or event related errors. On trace files, this is the total for the whole file.", colsize = 8, aggregation = "SUM" }, { name = "PID", description = "Process PID.", field = "proc.pid", colsize = 8, }, { tags = {"containers"}, name = "Container", field = "container.name", description = "Name of the container. What this field contains depends on the containerization technology. For example, for docker this is the content of the 'NAMES' column in 'docker ps'", colsize = 20 }, { name = "Command", description = "Full command line of the process.", field = "proc.exeline", aggregation = "MAX", colsize = 0 } }, actions = { { hotkey = "9", command = "kill -9 %proc.pid", description = "kill -9", ask_confirmation = true, wait_finish = false }, { hotkey = "c", command = "gcore %proc.pid", description = "generate core", }, { hotkey = "g", command = "gdb -p %proc.pid", description = "gdb attach", wait_finish = false }, { hotkey = "k", command = "kill %proc.pid", description = "kill", ask_confirmation = true, wait_finish = false }, { hotkey = "l", command = "ltrace -p %proc.pid", description = "ltrace", }, { hotkey = "s", command = "gdb -p %proc.pid --batch --quiet -ex \"thread apply all bt full\" -ex \"quit\"", description = "print stack", }, }, } sysdig-0.26.4/userspace/sysdig/chisels/v_procs_fd_usage.lua000066400000000000000000000072471352731327100240640ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "procs_fd_usage", name = "Processes FD Usage", description = "This view summarizes file descriptor usage for the processes in the system.", tips = { "A process that reaches its FD limit will very likely be killed by the OS. As a consequence, processes for which the OPEN column value is close to the MAX column value (or which, alternatively, have a PCT value close to 100) deserve particular attention.", "Clicking enter on a selection will show the activity I/O activity done by the process on different families of FDs."}, tags = {"Default"}, view_type = "table", filter = "evt.type!=switch", applies_to = {"", "container.id", "fd.name", "fd.containername", "fd.sport", "fd.sproto", "evt.type", "fd.directory", "fd.containerdirectory", "fd.type", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, is_root = true, drilldown_target = "io_by_type", use_defaults = true, columns = { { name = "NA", field = "proc.pid", is_key = true }, { name = "PID", description = "Process PID.", field = "proc.pid", colsize = 8, }, { tags = {"containers"}, name = "VPID", field = "proc.vpid", description = "PID that the process has inside the container.", colsize = 8, }, { name = "OPEN", field = "proc.fdopencount", description = "Number of open FDs that the process currently has. On a trace file, this is the maximum value reached by the process over the whole file.", aggregation = "MAX", colsize = 8, is_sorting = true, }, { name = "MAX", field = "proc.fdlimit", description = "Maximum number of FDs that this process can open.", aggregation = "MAX", colsize = 8, }, { name = "PCT", field = "proc.fdusage", description = "Percentage of currently open FDs versus the maximum allows for this process. In other words, this euquals to OPEN * 100 / MAX, and can be used to quickly identify processes that are getting close to their limit.", aggregation = "MAX", colsize = 8, }, { tags = {"containers"}, name = "The container this process belongs to.", field = "container.name", colsize = 20 }, { name = "Command", description = "The full command line of the process.", field = "proc.exeline", aggregation = "MAX", colsize = 0 } }, actions = { { hotkey = "9", command = "kill -9 %proc.pid", description = "kill -9", ask_confirmation = true, wait_finish = false }, { hotkey = "c", command = "gcore %proc.pid", description = "generate core", }, { hotkey = "g", command = "gdb -p %proc.pid", description = "gdb attach", wait_finish = false }, { hotkey = "k", command = "kill %proc.pid", description = "kill", ask_confirmation = true, wait_finish = false }, { hotkey = "l", command = "ltrace -p %proc.pid", description = "ltrace", }, { hotkey = "s", command = "gdb -p %proc.pid --batch --quiet -ex \"thread apply all bt full\" -ex \"quit\"", description = "print stack", }, }, } sysdig-0.26.4/userspace/sysdig/chisels/v_slow_io.lua000066400000000000000000000043511352731327100225450ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "slow_io", name = "Slow File I/O", description = "Lists all of the file read and write calls that took more than 1ms to complete, sorted based on completion time.", tags = {"Default", "wsysdig"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "thread.tid", "proc.name", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, filter = "evt.is_io=true and fd.type=file and (not fd.name contains '/dev/') and evt.latency>1000000", use_defaults = true, columns = { { name = "NA", field = "evt.rawtime", is_key = true }, { name = "TIME", field = "evt.time", description = "Time when the command was executed.", colsize = 12, }, { name = "LATENCY", field = "evt.latency", description = "The slow file name.", is_sorting = true, colsize = 8, }, { name = "FILENAME", field = "fd.name", description = "The slow file name.", colsize = 32, }, { tags = {"containers"}, name = "Container", field = "container.name", description = "Name of the container. What this field contains depends on the containerization technology. For example, for docker this is the content of the 'NAMES' column in 'docker ps'", colsize = 20 }, { name = "PID", field = "proc.pid", description = "PID of the process performing the I/O call.", colsize = 12, }, { name = "Command", field = "proc.exeline", aggregation = "MAX", description = "The command accessing the slow file, including arguments.", colsize = 0 } } } sysdig-0.26.4/userspace/sysdig/chisels/v_spans_list.lua000066400000000000000000000045771352731327100232630ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "spans_list", name = "Spans List", description = "Show the detailed list of a tracer selection's child spans. For each span type, the view reports information like its arguments and how long it took to complete.", tips = { "Only the spans spans that are direct childs of the selection (i.e. the spans with one more tag than the selection) are shown. Drilling down allows you to explore the further levels.", }, tags = {"Default"}, view_type = "table", applies_to = {"span.tag", "span.id", "span.time", "span.parenttime", "container.id", "proc.pid", "thread.nametid", "proc.name", "thread.tid", "fd.directory", "fd.containerdirectory", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, use_defaults = true, filter = "span.ntags>=%depth+1", drilldown_target = "spans_list", drilldown_increase_depth = true, columns = { { name = "NA", field = "span.rawtime", filterfield = "span.rawparenttime", is_key = true, filter_in_child_only = true, }, { name = "ID", field = "span.id", description = "the unique numeric ID of the span.", colsize = 10, }, { name = "TIME", field = "span.time", description = "the time of the span enter tracer.", colsize = 19, }, { name = "DURATION", field = "span.duration.fortag[%depth]", description = "the time this span call took to complete", colsize = 10, aggregation = "AVG", is_sorting = true, }, { name = "TAG", field = "span.tag[%depth]", description = "span tag.", colsize = 32, aggregation = "SUM" }, { name = "ARGS", field = "span.enterargs", description = "span enter arguments.", colsize = 256, aggregation = "SUM" }, } } sysdig-0.26.4/userspace/sysdig/chisels/v_spans_summary.lua000066400000000000000000000057261352731327100240020ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "spans_summary", name = "Spans Summary", description = "Show a summary of a tracer selection's child spans. For each span type, the view reports information like how many times it's been called and how long it took to complete.", tips = { "Only the spans spans that are direct childs of the selection (i.e. the spans with one more tag than the selection) are shown. Drilling down allows you to explore the further levels.", "This view collapses multiple spans with the same tag into a single entry. If you instead want to see each span as a separate entry, use the 'Spans List' view.", }, tags = {"Default"}, view_type = "table", applies_to = {"span.tag", "span.id", "span.time", "span.parenttime", "container.id", "proc.pid", "thread.nametid", "proc.name", "thread.tid", "fd.directory", "fd.containerdirectory", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, use_defaults = true, filter = "span.ntags>=%depth+1", drilldown_target = "spans_summary", spectro_type = "tracers", drilldown_increase_depth = true, columns = { { name = "NA", field = "span.tag[%depth]", is_key = true }, { is_sorting = true, name = "HITS", field = "span.count.fortag[%depth]", description = "number of times the span with the given tag has been hit.", colsize = 10, aggregation = "SUM" }, { name = "AVG TIME", field = "span.duration.fortag[%depth]", description = "the average time this span took to complete.", colsize = 10, aggregation = "AVG" }, { name = "MIN TIME", field = "span.duration.fortag[%depth]", description = "the minimum time this span took to complete.", colsize = 10, aggregation = "MIN" }, { name = "MAX TIME", field = "span.duration.fortag[%depth]", description = "the maximum time this span took to complete.", colsize = 10, aggregation = "MAX" }, { name = "CHD HITS", field = "span.childcount.fortag[%depth]", description = "number of times any child of the span with the given tag has been hit. This is useful to determine if the span is a leaf or if it has childs nested in it.", colsize = 10, aggregation = "SUM" }, { name = "TAG", field = "span.tag[%depth]", description = "span tag.", colsize = 256, aggregation = "SUM" }, } } sysdig-0.26.4/userspace/sysdig/chisels/v_spectro_all.lua000066400000000000000000000021761352731327100234040ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "spectro_all", name = "Spectrogram-All", description = "System call latency spectrogram.", view_type = "spectrogram", applies_to = {"evt.type"}, filter = "evt.dir=<", use_defaults = false, columns = { { name = "NA", field = "evt.latency.quantized", is_key = true }, { name = "LATENCY", description = "system call latency.", field = "evt.latency.quantized", }, { name = "COUNT", description = "XXX.", field = "evt.count", aggregation = "SUM", colsize = 8, } } } sysdig-0.26.4/userspace/sysdig/chisels/v_spectro_file.lua000066400000000000000000000027121352731327100235470ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "spectro_file", name = "Spectrogram-File", description = "File I/O latency spectrogram.", view_type = "spectrogram", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "thread.tid", "proc.name", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name", "fd.name", "fd.containername", "fd.directory", "fd.containerdirectory", "fd.containerdirectory"}, filter = "evt.dir=< and fd.type=file", use_defaults = false, columns = { { name = "NA", field = "evt.latency.quantized", is_key = true }, { name = "LATENCY", description = "file latency.", field = "evt.latency.quantized", }, { name = "COUNT", description = "XXX.", field = "evt.count", aggregation = "SUM", colsize = 8, } } } sysdig-0.26.4/userspace/sysdig/chisels/v_spectro_traces.lua000066400000000000000000000043471352731327100241170ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "spectro_traces", name = "Traces Spectrogram", description = "Traces duration spectrogram.", tips = { "This view offers a spectrogram-based representation of root trace spans durations.", "When appled to a selection in a view like 'Trace Summary' or 'Trace List', this view will only show the latency of the selected spans, while their parent and child spans won't be shown. When applied to the whole machine, this view will show the latency of the traces, i.e. the root spans that have just one tag.", "If you are in a trace view like 'Traces Summary' or 'Traces List', you can quickly show this spectrogram for a selection by clicking on F12.", }, view_type = "spectrogram", applies_to = {"", "span.tag", "span.id", "container.id", "proc.pid", "thread.nametid", "thread.tid", "proc.name", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name", "fd.name", "fd.containername", "fd.directory", "fd.containerdirectory", "fd.containerdirectory"}, filter = "span.ntags=%depth+1", use_defaults = false, drilldown_target = "spans_list", propagate_filter = false, columns = { { name = "NA", field = "span.duration.quantized", is_key = true }, { name = "LATENCY", description = "span latency. This determines the horizontal position of a dot in the chart.", field = "span.duration.quantized", }, { name = "COUNT", description = "number of times a span falls in a certain latency bucket. This determines the color of a dot in the chart.", field = "evt.count", aggregation = "SUM", } } } sysdig-0.26.4/userspace/sysdig/chisels/v_sports.lua000066400000000000000000000044421352731327100224250ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "sports", name = "Server Ports", description = "This view lists all of the server ports in terms of network bandwidth usage.", tips = {"Want to restrict this visualization to a single process or container? Just drill down into them before applying it.", "Select a port and drill down with the 'Top Processes' view to see which processes are generating traffic on a port."}, tags = {"wsysdig"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "thread.tid", "proc.name", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, filter = "(fd.type=ipv4 or fd.type=ipv6) and fd.name!=''", use_defaults = true, drilldown_target = "connections", columns = { { name = "NA", field = "fd.sport", is_key = true }, { name = "SPORT", description = "Server Port.", field = "fd.sport", colsize = 8, }, { name = "BPS IN", field = "evt.buflen.net.in", description = "This port's input bandwidth in bytes per second.", is_sorting = true, colsize = 12, aggregation = "TIME_AVG" }, { name = "BPS OUT", field = "evt.buflen.net.out", description = "This port's output bandwidth in bytes per second.", colsize = 12, aggregation = "TIME_AVG" }, { name = "IO CALLS", field = "evt.count", description = "Total (read+write) number of input/output calls made by the process on the connection.", colsize = 12, aggregation = "SUM" } }, actions = { { hotkey = "t", command = "tcpdump -niany port %fd.sport", description = "tcpdump port", wait_finish = false }, }, } sysdig-0.26.4/userspace/sysdig/chisels/v_spy_syslog.lua000066400000000000000000000041241352731327100233030ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "spy_syslog", name = "Spy Syslog", description = "Show the entries written to syslog.", tips = {"This view can be applied to the whole system, to watch overall syslog activity, but is also useful when applied to a container or a process. In that case, the view will only show the syslog writes generated by the selected entity."}, tags = {"Default"}, view_type = "list", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, filter = "fd.name contains /dev/log and evt.is_io_write=true and evt.dir=< and evt.failed=false", columns = { { name = "PID", field = "proc.pid", description = "PID of the process generating the message.", colsize = 8, }, { name = "PROC", field = "proc.name", description = "Name of the process generating the message.", colsize = 8, }, { name = "FAC", field = "syslog.facility.str", description = "syslog facility of the message.", colsize = 8, }, { name = "SEV", field = "syslog.severity.str", description = "syslog severity of the message.", colsize = 8, }, { tags = {"containers"}, name = "Container", field = "container.name", colsize = 20 }, { name = "MESSAGE", field = "syslog.message", description = "Message sent to syslog", colsize = 0 } } } sysdig-0.26.4/userspace/sysdig/chisels/v_spy_users.lua000066400000000000000000000044151352731327100231270ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "spy_users", name = "Spy Users", description = "Lists all the commands that are run interactively, i.e. that have a shell as the parent process. The result is the display of all the user activity, sorted by time.", tags = {"Default"}, view_type = "list", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "thread.tid", "proc.name", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, filter = "((evt.type=execve and evt.dir=<) or (evt.type=chdir and evt.dir=< and proc.name contains sh and not proc.name contains sshd)) and evt.failed=false", use_defaults = true, columns = { { name = "TIME", field = "evt.time", description = "Time when the command was executed.", colsize = 12, }, { name = "USER", field = "user.name", description = "Name of the user running the command.", colsize = 12, }, { name = "SHELL", field = "proc.ppid", description = "Pid of the shell where this command was executed. This, essentially, corresponds to a 'session ID'. You can filer or sort by this column to isolate a specific interactive user session.", colsize = 8, }, { tags = {"containers"}, name = "Container", field = "container.name", description = "Name of the container. What this field contains depends on the containerization technology. For example, for docker this is the content of the 'NAMES' column in 'docker ps'", colsize = 20 }, { name = "Command", field = "proc.exeline", aggregation = "MAX", description = "The executed command, including arguments.", colsize = 0 } } } sysdig-0.26.4/userspace/sysdig/chisels/v_spy_users_wsysdig.lua000066400000000000000000000047041352731327100247010ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "spy_users_wsysdig", name = "Spy Users", description = "Lists all the commands that are run interactively, i.e. that have a shell as the parent process. The result is the display of all the user activity, sorted by time.", tags = {"Default", "wsysdig", "nocsysdig"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "thread.tid", "proc.name", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, filter = "((evt.type=execve and evt.dir=<) or (evt.type=chdir and evt.dir=< and proc.name contains sh and not proc.name contains sshd)) and evt.failed=false", drilldown_target = "threads", use_defaults = true, propagate_filter = false, columns = { { name = "NA", field = "thread.nametid", is_key = true }, { name = "TIME", field = "evt.time", description = "Time when the command was executed.", colsize = 12, is_sorting = true }, { name = "USER", field = "user.name", description = "Name of the user running the command.", colsize = 12, }, { name = "SHELL", field = "proc.ppid", description = "Pid of the shell where this command was executed. This, essentially, corresponds to a 'session ID'. You can filer or sort by this column to isolate a specific interactive user session.", colsize = 8, }, { tags = {"containers"}, name = "Container", field = "container.name", description = "Name of the container. What this field contains depends on the containerization technology. For example, for docker this is the content of the 'NAMES' column in 'docker ps'", colsize = 20 }, { name = "Command", field = "proc.exeline", aggregation = "MAX", description = "The executed command, including arguments.", colsize = 0 } } } sysdig-0.26.4/userspace/sysdig/chisels/v_syscall_procs.lua000066400000000000000000000035551352731327100237570ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "syscall_procs", name = "Syscall Callers", description = "Show the top processes based on number of system call invocations and time spent calling them.", tags = {"Default"}, view_type = "table", applies_to = {"evt.type"}, use_defaults = true, filter = "syscall.type exists", columns = { { name = "NA", field = "proc.pid", is_key = true }, { is_sorting = true, name = "CALLS/S", field = "evt.count", description = "Number of system calls per second that this process has invoked.", colsize = 10, aggregation = "TIME_AVG" }, { name = "TIME", field = "evt.latency", description = "Total time spent on system calls by the process during the sample interval. On trace files, this is the total for the whole file.", colsize = 10, aggregation = "SUM" }, { tags = {"containers"}, name = "Container", field = "container.name", description = "Name of the container. What this field contains depends on the containerization technology. For example, for docker this is the content of the 'NAMES' column in 'docker ps'", colsize = 20 }, { name = "Command", description = "The full command line of the process.", field = "proc.exeline", aggregation = "MAX", colsize = 0 } } } sysdig-0.26.4/userspace/sysdig/chisels/v_syscalls.lua000066400000000000000000000046271352731327100227350ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "syscalls", name = "System Calls", description = "Show the top system calls in the system based on number of invocations and time spent calling them.", tips = { "This view is useful to spot not only system activity saturation, but also things like high wait time.", "Drill down by clicking enter on a system call to see which processes are using it.", "The AVG TIME column is useful to identify system operations that tend to be consistently slow and can be the cause of bottlenecks."}, tags = {"Default", "wsysdig"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "fd.name", "fd.containername", "fd.directory", "fd.containerdirectory", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, use_defaults = true, filter = "syscall.type exists", drilldown_target = "syscall_procs", columns = { { name = "NA", field = "evt.type", is_key = true }, { is_sorting = true, name = "CALLS/S", field = "evt.count", description = "Number of calls per second for this system call.", colsize = 10, aggregation = "TIME_AVG" }, { name = "TOT TIME", field = "evt.latency", description = "Total time spent waiting for the given system call to return.", colsize = 10, aggregation = "SUM" }, { name = "AVG TIME", field = "evt.latency", description = "Average time spent in the given system call. This is calculated dividing the value under TOT TIME by the value under COUNT.", colsize = 10, aggregation = "AVG" }, { name = "SYSCALL", field = "evt.type", description = "System call name.", colsize = 32, aggregation = "SUM" }, } } sysdig-0.26.4/userspace/sysdig/chisels/v_threads.lua000066400000000000000000000062001352731327100225170ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "threads", name = "Threads", description = "This view lists all the threads running in the system or in the current selection, showing usage of resources like CPU, memory, disk and network for each thread.", tips = {"Apply this view to a process to get the list of threads for that process only. Similarly, apply it to a container to see the threads running inside it."}, tags = {"Default", "wsysdig"}, view_type = "table", filter = "evt.type!=switch", applies_to = {"", "proc.pid", "thread.nametid", "proc.name", "container.id", "fd.sport", "fd.sproto", "fd.name", "fd.containername", "fd.directory", "fd.containerdirectory", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, is_root = true, drilldown_target = "files", use_defaults = true, columns = { { name = "NA", field = "thread.tid", is_key = true }, { name = "PID", description = "PID of the process this thread belongs to.", field = "proc.pid", colsize = 8, }, { name = "TID", description = "Thread-specific ID. Main threads have TID=PID.", field = "thread.tid", colsize = 8, }, { tags = {"containers"}, name = "VPID", field = "proc.vpid", description = "PID that the process has inside the container.", colsize = 8, }, { tags = {"containers"}, name = "VTID", field = "thread.vtid", description = "TID that the tread has inside the container.", colsize = 8, }, { name = "CPU", field = "thread.cpu", description = "Amount of CPU used by the proccess.", colsize = 8, aggregation = "AVG", is_sorting = true }, { name = "FILE", field = "evt.buflen.file", description = "Total (input+output) file I/O bandwidth generated by the thread, in bytes per second.", colsize = 8, aggregation = "TIME_AVG" }, { name = "NET", field = "evt.buflen.net", description = "Total (input+output) network I/O bandwidth generated by the thread, in bytes per second.", colsize = 8, aggregation = "TIME_AVG" }, { tags = {"containers"}, name = "Container", field = "container.name", description = "Name of the container. What this field contains depends on the containerization technology. For example, for docker this is the content of the 'NAMES' column in 'docker ps'", colsize = 20 }, { name = "Command", description = "The full command line of the process.", field = "proc.exeline", aggregation = "MAX", colsize = 0 } } } sysdig-0.26.4/userspace/sysdig/chisels/v_traces_list.lua000066400000000000000000000051041352731327100234030ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "traces_list", name = "Traces List", description = "Show the detailed list of the traces executing in the system. For each single executed trace, the view reports information like the timestamp, the duration and the arguments.", tips = { "Traces are sysdig's super easy way to delimit portions of your code so that sysdig can measure how long they take and tell you what's happening inside them. You can learn about tracers at https://github.com/draios/sysdig/wiki/Tracers.", "Only the root trace spans (i.e. the spans with only one tag) are shown when this view is applied to the whole machine. Drilling down allows you to explore the child spans.", }, tags = {"Default"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "proc.name", "thread.tid", "fd.directory", "fd.containerdirectory", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, use_defaults = false, filter = "span.ntags>=%depth+1", drilldown_target = "spans_list", drilldown_increase_depth = true, columns = { { name = "NA", field = "span.rawtime", filterfield = "span.rawparenttime", is_key = true, filter_in_child_only = true, }, { name = "ID", field = "span.id", description = "the unique numeric ID of the span.", colsize = 10, }, { name = "TIME", field = "span.time", description = "the time of the span enter tracer.", colsize = 19, }, { name = "DURATION", field = "span.duration.fortag[%depth]", description = "the time this span call took to complete", colsize = 10, aggregation = "AVG", is_sorting = true, }, { name = "TAG", field = "span.tag[%depth]", description = "trace tag.", colsize = 32, aggregation = "SUM" }, { name = "ARGS", field = "span.enterargs", description = "trace enter arguments.", colsize = 256, aggregation = "SUM" }, } } sysdig-0.26.4/userspace/sysdig/chisels/v_traces_summary.lua000066400000000000000000000063121352731327100241270ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] view_info = { id = "traces_summary", name = "Traces Summary", description = "Show a summary of the traces executing in the system. For each trace tag, the view reports information like how many spans with that tag have executed and what's the average duration.", tips = { "Traces are sysdig's super easy way to delimit portions of your code so that sysdig can measure how long they take and tell you what's happening inside them. You can learn about tracers at https://github.com/draios/sysdig/wiki/Tracers.", "Only the root trace spans (i.e. the spans with only one tag) are shown when this view is applied to the whole machine. Drilling down allows you to explore the child spans.", "This view collapses multiple spans with the same tag into a single entry, offering a compact summary of trace activity. If you instead want to see each span as a separate entry, use the 'Trace List' view.", }, tags = {"Default"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "thread.nametid", "proc.name", "thread.tid", "fd.directory", "fd.containerdirectory", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.rs.id", "k8s.svc.id", "k8s.ns.id", "marathon.app.id", "marathon.group.name", "mesos.task.id", "mesos.framework.name"}, use_defaults = true, filter = "span.ntags>=%depth+1", drilldown_target = "spans_summary", spectro_type = "tracers", drilldown_increase_depth = true, columns = { { name = "NA", field = "span.tag[%depth]", is_key = true }, { is_sorting = true, name = "HITS", field = "span.count.fortag[%depth]", description = "number of times the trace with the given tag has been hit.", colsize = 10, aggregation = "SUM" }, { name = "AVG TIME", field = "span.duration.fortag[%depth]", description = "the average time this trace took to complete.", colsize = 10, aggregation = "AVG" }, { name = "MIN TIME", field = "span.duration.fortag[%depth]", description = "the minimum time this trace took to complete.", colsize = 10, aggregation = "MIN" }, { name = "MAX TIME", field = "span.duration.fortag[%depth]", description = "the maximum time this trace took to complete.", colsize = 10, aggregation = "MAX" }, { name = "CHD HITS", field = "span.childcount.fortag[%depth]", description = "number of times any child of the trace with the given tag has been hit. This is useful to determine if the span is a leaf or if it has childs nested in it.", colsize = 10, aggregation = "SUM" }, { name = "TAG", field = "span.tag[%depth]", description = "span tag.", colsize = 256, aggregation = "SUM" }, } } sysdig-0.26.4/userspace/sysdig/chisels/wsysdig_summary.lua000066400000000000000000001245771352731327100240300ustar00rootroot00000000000000--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. --]] -- Chisel description description = "internal chisel, creates the json for the wsysdig summary page." short_description = "wsysdig summary generator" category = "NA" hidden = true -- Imports and globals require "common" -- Chisel argument list args = { { name = "composite_args", description = "The number of events in the file. If this argument is not specified, the chisel will just scan the file, compute the number of events and then relaunch itself with the number as argument.", argtype = "int", optional = true } } local disable_index = true -- change this if you are working on this script and don't want to be bothered by indexing local n_samples = 400 local sampling_period = 0 local arg_n_timeline_samples = n_samples local json = require ("dkjson") local gsummary = {} -- The global summary local ssummary = {} -- Last sample's summary local nintervals = 0 local file_cache_exists = false local arg_file_duration = nil local evtcnt = 0 local index_format_version = 1 -- Increase this if the content or the format of the output changes. -- An increase in this number will cause existing indexes to be discharged. -- Argument notification callback function on_set_arg(name, val) if name == "composite_args" then vals = split(val, ",") local val1n = tonumber(vals[1]) if val1n ~= 0 and val1n < n_samples then arg_n_timeline_samples = val1n end if vals[2] ~= nil then arg_file_duration = vals[2] end return true end return false end ------------------------------------------------------------------------------- -- Summary handling helpers ------------------------------------------------------------------------------- local services = { [80] = 'HTTP', [8080] = 'HTTP', [443] = 'HTTPs', [22] = 'SSH', [53] = 'DNS', [6666] = 'Sysdig Agent', [6667] = 'Sysdig Agent', [6443] = 'Sysdig Agent', [2379] = 'etcd', [22379] = 'etcd', [3306] = 'mysql', [5432] = 'postgres', [6379] = 'redis', [5984] = 'couchdb', [9880] = 'fluentd', [8125] = 'statsd', [4730] = 'gearman', [50070] = 'hadoop', [8020] = 'hadoop', [9000] = 'hadoop', [60000] = 'hbase', [60010] = 'hbase', [60020] = 'hbase', [60030] = 'hbase', [2181] = 'kafka', [1978] = 'Kyoto Tycoon', [11211] = 'memcached', [27017] = 'mongodb', [27018] = 'mongodb', [27019] = 'mongodb', [28017] = 'mongodb', [5672] = 'rabbitmq', [8087] = 'riak', [8098] = 'riak', [8983] = 'solr', [5555] = 'voltdb' } -- -- Populate the protocol table for a crappy application that use a ton of ports -- for j=9200,9400,1 do services[j] = 'elasticsearch' end -- -- Create the protocols table by inverting the services table -- local protocols = {} for i, v in pairs(services) do if protocols[v] == nil then protocols[v] = {i} else protocols[v][#protocols[v] + 1] = i end end function create_category_basic(excludable, noteworthy, aggregation) aggregation = aggregation or 'sum' return { tot=0, max=0, timeLine={}, excludable=excludable, noteworthy=noteworthy, aggregation=aggregation } end function create_category_table(excludable, noteworthy, aggregation) aggregation = aggregation or 'sum' return { tot=0, max=0, timeLine={}, table={}, excludable=excludable, noteworthy=noteworthy, aggregation=aggregation } end function reset_summary(s) s.procCount = create_category_table(false, false, 'avg') s.containerCount = create_category_table(false, false, 'avg') s.executedCommands = create_category_basic(false, true) s.executedInteractiveCommands = create_category_basic(true, true) s.syscallCount = create_category_basic(false, false) s.fileCount = create_category_table(true, false) s.fileBytes = create_category_basic(false, false) s.fileBytesR = create_category_basic(false, false) s.fileBytesW = create_category_basic(false, false) s.fileCountW = create_category_table(true, false) s.sysFileCountW = create_category_table(true, true) s.connectionCount = create_category_table(true, false) s.netBytes = create_category_basic(false, false) s.netBytesR = create_category_basic(false, false) s.netBytesW = create_category_basic(false, false) s.notifications = create_category_basic(true, true) if s.listeningPortCount == nil then s.listeningPortCount = create_category_table(true, false, 'avg') end s.newConnectionsO = create_category_basic(true, false) s.newConnectionsI = create_category_basic(true, false) s.newConnectionsSsh = create_category_basic(true, true) s.newListeningPorts = create_category_basic(true, true) s.fileDeletionsCount = create_category_basic(true, true) s.newSymLinksCount = create_category_basic(true, true) s.forkCount = create_category_basic(true, false) s.openErrorCount = create_category_basic(true, false) s.connectErrorCount = create_category_basic(true, true) s.sudoInvocations = create_category_basic(true, true) s.setnsInvocations = create_category_basic(true, true) s.signalCount = create_category_basic(true, false) s.segfaultCount = create_category_basic(true, true) s.over1msFileIoCount = create_category_basic(true, false) s.over10msFileIoCount = create_category_basic(true, false) s.over100msFileIoCount = create_category_basic(true, true) s.appLogCount = create_category_basic(true, false) s.appLogCountW = create_category_basic(true, false) s.appLogCountE = create_category_basic(true, true) s.sysLogCount = create_category_basic(true, false) s.sysLogCountW = create_category_basic(true, false) s.sysLogCountE = create_category_basic(true, true) s.dockerEvtsCount = create_category_basic(true, true) -- reset dynamic dockerEvtsCount* categories for ccat in pairs(s) do prefix = 'dockerEvtsCount' if starts_with(ccat, prefix) and ccat ~= prefix then s[ccat] = create_category_basic(true, true) end end s.sysReqCountHttp = create_category_basic(true, true) s.sysErrCountHttp = create_category_basic(true, true) -- creating the protocol categories involves two passes of the services table for i, v in pairs(protocols) do local ccat = 'protoBytes_' .. i s[ccat] = create_category_basic(true, false) end end function add_summaries(ts_s, ts_ns, dst, src) local time = sysdig.make_ts(ts_s, ts_ns) for k, v in pairs(src) do if dst[k] == nil then -- add missing category dynamically -- dynamic categories are dockerEvtsCount* prefix = 'dockerEvtsCount' if starts_with(k, prefix) and k ~= prefix then dst[k] = create_category_basic(true, true) end end dst[k].tot = dst[k].tot + v.tot if v.tot > dst[k].max then dst[k].max = v.tot end local tl = dst[k].timeLine tl[#tl+1] = {t=time, v=v.tot} if v.table ~= nil then local dt = dst[k].table for tk, tv in pairs(v.table) do dt[tk] = tv end end end end function generate_subsampled_timeline(src, nsamples, op) local res = {} local ratio = math.ceil(#src / nsamples) local k = 0 local accumulator = 0 local etime = src[1].t local max = 0 local tot = 0 for j = 1,#src,1 do k = k + 1 accumulator = accumulator + src[j].v if k >= ratio then if op == 'avg' then accumulator = accumulator / k end res[#res+1] = {t=etime, v=accumulator} tot = tot + accumulator if accumulator > max then max = accumulator end k = 0 accumulator = 0 if src[j + 1] ~= nil then etime = src[j + 1].t end end end return{timeLine=res, tot=tot, max=max} end function subsample_timelines(jtable) if arg_n_timeline_samples ~= 0 and arg_n_timeline_samples ~= n_samples then for k, v in pairs(jtable.metrics) do local data = v.data st = generate_subsampled_timeline(data.timeLine, arg_n_timeline_samples, data.aggregation) v.data.timeLine = st.timeLine v.data.max = st.max end end end ------------------------------------------------------------------------------- -- Helpers to dig into the data coming from sysdig ------------------------------------------------------------------------------- function string.starts(big_str, small_str) return string.sub(big_str, 1, string.len(small_str)) == small_str end function is_system_dir(filename) if string.starts(filename, '/bin/') or string.starts(filename, '/sbin/') or string.starts(filename, '/boot/') or string.starts(filename, '/etc/') or string.starts(filename, '/lib') or string.starts(filename, '/usr/bin/') or string.starts(filename, '/usr/sbin/') or string.starts(filename, '/usr/share/') or string.starts(filename, '/usr/lib') then return true end return false end function is_log_file(filename) if(string.find(filename, '%.log') or string.find(filename, '_log') or string.find(filename, '/var/log')) and not (string.find(filename, '%.gz') or string.find(filename, '%.tgz')) then return true end return false end function generate_io_stats(fdname, cnt_cat) if fdname == nil then return end if cnt_cat.table[fdname] == nil then cnt_cat.table[fdname] = 1 cnt_cat.tot = cnt_cat.tot + 1 end end function generate_proto_stats(sport, buflen) local proto = services[sport] if proto ~= nil then local catname = 'protoBytes_' .. proto ssummary[catname].tot = ssummary[catname].tot + buflen end end function parse_thread_table_startup() local data = {} local cnt = 0 local ttable = sysdig.get_thread_table_barebone(sysdig.get_filter()) for k, v in pairs(ttable) do for kf, vf in pairs(v.fdtable) do if vf.is_server then data[vf.sport] = 1 end end end ssummary.listeningPortCount.tot = 0 for k, v in pairs(data) do ssummary.listeningPortCount.tot = ssummary.listeningPortCount.tot + 1 end --print(ssummary.listeningPortCount.tot) ssummary.listeningPortCount.table = data end function parse_thread_table_interval() local data = {} local cnt = 0 local ttable = sysdig.get_thread_table_barebone_nofds(sysdig.get_filter()) for k, v in pairs(ttable) do if v.tid == v.pid then data[v.pid] = 1 cnt = cnt + 1 end end ssummary.procCount.tot = cnt ssummary.procCount.table = data end function parse_container_table() local data = {} local cnt = 0 local ctable = sysdig.get_container_table() for k, v in pairs(ctable) do data[v.id] = v.name cnt = cnt + 1 end ssummary.containerCount.tot = cnt ssummary.containerCount.table = data end function update_docker_cats(evt_type) local cat = 'dockerEvtsCount' .. evt_type if (ssummary[cat] == nil) then ssummary[cat] = create_category_basic(true, true) end ssummary[cat].tot = ssummary[cat].tot + 1 end ------------------------------------------------------------------------------- -- Initialization callbacks ------------------------------------------------------------------------------- function on_init() if arg_file_duration == nil then return true end if(sysdig.get_filter() ~= nil and sysdig.get_filter() ~= '') then disable_index = true end sampling_period = arg_file_duration / (n_samples - 1) chisel.set_precise_interval_ns(sampling_period) percent_update_sample_period = math.floor(n_samples / 100 * 3) if percent_update_sample_period < 2 then percent_update_sample_period = 1 end reset_summary(gsummary) reset_summary(ssummary) -- set the following fields on_event() fetype = chisel.request_field("evt.type") fdir = chisel.request_field("evt.dir") frawres = chisel.request_field("evt.rawres") ffdcontname = chisel.request_field("fd.containername") ffdname = chisel.request_field("fd.name") ffdtype = chisel.request_field("fd.type") fiswrite = chisel.request_field("evt.is_io_write") fisread = chisel.request_field("evt.is_io_read") fbuffer = chisel.request_field("evt.buffer") fbuflen = chisel.request_field("evt.buflen") fsport = chisel.request_field("fd.sport") flport = chisel.request_field("fd.lport") ftypechar = chisel.request_field("fd.typechar") fexe = chisel.request_field("evt.arg.exe") fsignal = chisel.request_field("evt.arg.sig") flatency = chisel.request_field("evt.latency") fsyslogsev = chisel.request_field("syslog.severity") finfrasource = chisel.request_field("evt.arg.source") finfraname = chisel.request_field("evt.arg.name") fpname = chisel.request_field("proc.pname") print('{"slices": [') return true end function on_capture_start() if arg_file_duration == nil then return true end if not disable_index then local dirname = sysdig.get_evtsource_name() .. '_wd_index' local f = io.open(dirname .. '/VERSION', "r") if f ~= nil then local version = tonumber(f:read "*all") f:close() if version == index_format_version then file_cache_exists = true sysdig.end_capture() end end end parse_thread_table_startup() return true end ------------------------------------------------------------------------------- -- Event callback ------------------------------------------------------------------------------- function on_event() if arg_file_duration == nil then evtcnt = evtcnt + 1 return true end ssummary.syscallCount.tot = ssummary.syscallCount.tot + 1 local dir = evt.field(fdir) if dir ~= nil then if dir == '<' then local rawres = evt.field(frawres) local etype = evt.field(fetype) if rawres ~= nil and rawres >= 0 then local fdcontname = evt.field(ffdcontname) local fdname = evt.field(ffdname) local fdtype = evt.field(ffdtype) local iswrite = evt.field(fiswrite) local isread = evt.field(fisread) if iswrite or isread then if fdtype == 'file' then local buflen = evt.field(fbuflen) if buflen == nil then buflen = 0 end generate_io_stats(fdcontname, ssummary.fileCount) if iswrite then generate_io_stats(fdcontname, ssummary.fileCountW) ssummary.fileBytes.tot = ssummary.fileBytes.tot + buflen ssummary.fileBytesW.tot = ssummary.fileBytesW.tot + buflen if is_system_dir(fdname) then generate_io_stats(fdname, ssummary.sysFileCountW) end -- log metrics support local syslogsev = evt.field(fsyslogsev) if syslogsev ~= nil then ssummary.sysLogCount.tot = ssummary.sysLogCount.tot + 1 if syslogsev == 4 then ssummary.sysLogCountW.tot = ssummary.sysLogCountW.tot + 1 elseif syslogsev < 4 then ssummary.sysLogCountE.tot = ssummary.sysLogCountE.tot + 1 end elseif is_log_file(fdname) then local buf = evt.field(fbuffer) local msgs = split(buf, "\n") for i, msg in ipairs(msgs) do if #msg ~= 0 then ssummary.appLogCount.tot = ssummary.appLogCount.tot + 1 local ls = string.lower(msg) if string.find(ls, "warn") then ssummary.appLogCountW.tot = ssummary.appLogCountW.tot + 1 elseif string.find(ls, "error") or string.find(ls, "critic") or string.find(ls, "emergency") or string.find(ls, "alert") then ssummary.appLogCountE.tot = ssummary.appLogCountE.tot + 1 end end end end elseif isread then ssummary.fileBytes.tot = ssummary.fileBytes.tot + buflen ssummary.fileBytesR.tot = ssummary.fileBytesR.tot + buflen end local latency = evt.field(flatency) if latency ~= nil and not string.starts(fdname, '/dev/') then if latency > 100000000 then ssummary.over100msFileIoCount.tot = ssummary.over100msFileIoCount.tot + 1 end if latency > 10000000 then ssummary.over10msFileIoCount.tot = ssummary.over10msFileIoCount.tot + 1 end if latency > 1000000 then ssummary.over1msFileIoCount.tot = ssummary.over1msFileIoCount.tot + 1 end end elseif fdtype == 'ipv4' or fdtype == 'ipv6' then local buflen = evt.field(fbuflen) if buflen == nil then buflen = 0 end generate_io_stats(fdcontname, ssummary.connectionCount) if iswrite then ssummary.netBytes.tot = ssummary.netBytes.tot + buflen ssummary.netBytesW.tot = ssummary.netBytesW.tot + buflen elseif isread then ssummary.netBytes.tot = ssummary.netBytes.tot + buflen ssummary.netBytesR.tot = ssummary.netBytesR.tot + buflen end local sport = evt.field(fsport) if sport ~= nil then generate_proto_stats(sport, buflen) end local buf = evt.field(fbuffer) if string.starts(buf, 'HTTP/') then ssummary.sysReqCountHttp.tot = ssummary.sysReqCountHttp.tot + 1 local parts = split(buf, ' ') if tonumber(parts[2]) ~= 200 then ssummary.sysErrCountHttp.tot = ssummary.sysErrCountHttp.tot + 1 end end elseif fdtype == 'unix' then if iswrite then -- apps can write to syslog using unix pipes local syslogsev = evt.field(fsyslogsev) if syslogsev ~= nil then ssummary.sysLogCount.tot = ssummary.sysLogCount.tot + 1 if syslogsev == 4 then ssummary.sysLogCountW.tot = ssummary.sysLogCountW.tot + 1 elseif syslogsev < 4 then ssummary.sysLogCountE.tot = ssummary.sysLogCountE.tot + 1 end end end end elseif etype == 'execve' then ssummary.executedCommands.tot = ssummary.executedCommands.tot + 1 local pname = evt.field(fpname) if pname ~= nil then if string.find(pname, 'bash') then ssummary.executedInteractiveCommands.tot = ssummary.executedInteractiveCommands.tot + 1 end end local exe = evt.field(fexe) if exe == 'sudo' then ssummary.sudoInvocations.tot = ssummary.sudoInvocations.tot + 1 end elseif etype == 'bind' then local sport = evt.field(fsport) if sport ~= nil then generate_io_stats(sport, ssummary.listeningPortCount) ssummary.newListeningPorts.tot = ssummary.newListeningPorts.tot + 1 end elseif etype == 'connect' then local sport = evt.field(fsport) if sport ~= nil then ssummary.newConnectionsO.tot = ssummary.newConnectionsO.tot + 1 if sport == 22 then ssummary.newConnectionsSsh.tot = ssummary.newConnectionsSsh.tot + 1 end end elseif etype == 'accept' then local sport = evt.field(fsport) if sport ~= nil then ssummary.newConnectionsI.tot = ssummary.newConnectionsI.tot + 1 if sport == 22 then ssummary.newConnectionsSsh.tot = ssummary.newConnectionsSsh.tot + 1 end end elseif etype == 'unlink' or etype == 'unlinkat' then ssummary.fileDeletionsCount.tot = ssummary.fileDeletionsCount.tot + 1 elseif etype == 'symlink' or etype == 'symlinkat' then ssummary.newSymLinksCount.tot = ssummary.newSymLinksCount.tot + 1 elseif etype == 'clone' or etype == 'fork' then if rawres > 0 then ssummary.forkCount.tot = ssummary.forkCount.tot + 1 end elseif etype == 'setns' then ssummary.setnsInvocations.tot = ssummary.setnsInvocations.tot + 1 end elseif etype == 'connect' then local sport = evt.field(fsport) if sport ~= nil then ssummary.newConnectionsO.tot = ssummary.newConnectionsO.tot + 1 if sport == 22 then ssummary.newConnectionsSsh.tot = ssummary.newConnectionsSsh.tot + 1 end end if rawres ~= -115 then local fdtype = evt.field(ffdtype) if fdtype == 'ipv4' or fdtype == 'ipv6' then ssummary.connectErrorCount.tot = ssummary.connectErrorCount.tot + 1 end end elseif etype == 'accept' then local sport = evt.field(fsport) if sport ~= nil then ssummary.newConnectionsI.tot = ssummary.newConnectionsI.tot + 1 if sport == 22 then ssummary.newConnectionsSsh.tot = ssummary.newConnectionsSsh.tot + 1 end end elseif etype == 'open' then ssummary.openErrorCount.tot = ssummary.openErrorCount.tot + 1 end else local etype = evt.field(fetype) if etype == 'close' then local sport = evt.field(fsport) if sport ~= nil then local typechar = evt.field(ftypechar) if typechar == '2' then if ssummary.listeningPortCount.table[sport] ~= nil then ssummary.listeningPortCount.table[sport] = nil ssummary.listeningPortCount.tot = ssummary.listeningPortCount.tot - 1 end end end elseif etype == 'signaldeliver' then ssummary.signalCount.tot = ssummary.signalCount.tot + 1 local signal = evt.field(fsignal) if signal == 'SIGSEGV' then ssummary.segfaultCount.tot = ssummary.segfaultCount.tot + 1 end elseif etype == 'notification' then ssummary.notifications.tot = ssummary.notifications.tot + 1 elseif etype == 'infra' then local infrasource = evt.field(finfrasource) if infrasource == 'docker' then ssummary.dockerEvtsCount.tot = ssummary.dockerEvtsCount.tot + 1 local infraname = evt.field(finfraname) update_docker_cats(infraname) end end end end return true end ------------------------------------------------------------------------------- -- Periodic timeout callback ------------------------------------------------------------------------------- function on_interval(ts_s, ts_ns, delta) parse_thread_table_interval() parse_container_table() --print(json.encode(ssummary.connectionCount, { indent = true })) add_summaries(ts_s, ts_ns, gsummary, ssummary) reset_summary(ssummary) if nintervals % percent_update_sample_period == 0 then local progress = sysdig.get_read_progress() if progress == 100 then progress = 99.99 end print('{"progress": ' .. progress .. ' },') io.flush(stdout) end nintervals = nintervals + 1 return true end ------------------------------------------------------------------------------- -- End of capture output generation ------------------------------------------------------------------------------- function update_table_count(cat) if cat.table ~= nil then local cnt = 0 for tk, tv in pairs(cat.table) do cnt = cnt + 1 end cat.tot = cnt cat.table = nil end end function update_table_counts() for k, v in pairs(gsummary) do update_table_count(v) end end function should_include(category) if category.excludable then if category.tot ~= 0 then return true else return false end else return true end end function get_category_table(include_network_apps, include_security, include_performance, include_logs, include_infrastructure) local res = { {id='general', name='General'}, {id='file', name='File'}, {id='network', name='Network'}, } if include_network_apps then res[#res+1] = {id='napps', name='Network Apps'} end if include_security then res[#res+1] = {id='security', name='Security'} end if include_performance then res[#res+1] = {id='performance', name='Performance'} end if include_logs then res[#res+1] = {id='logs', name='Logs'} end if include_infrastructure then res[#res+1] = {id='infrastructure', name='Infrastructure'} end return res end function build_output(captureDuration) local ctable = copytable(gsummary.containerCount.table) local res = {} local has_cat_logs = false; local has_cat_infrastructure = false; local has_cat_netapps = false; local jtable = { info={ IndexFormatVersion=index_format_version, containers=ctable, durationNs=captureDuration, startTs = sysdig.get_firstevent_ts(), endTs = sysdig.get_lastevent_ts() }, metrics=res} local filter = sysdig.get_filter() update_table_counts() if should_include(gsummary.notifications) then res[#res+1] = { name = 'Sysdig Secure Notifications', desc = 'Sysdig Secure notifications. Sysdig secure inserts a "notification" event in the capture stream each time a policy triggers. This metric counts the notifications. Chart it over time to compare the other metrics with the point in time where policies were triggered.', category = 'general', targetView = 'notifications', drillDownKey = 'NONE', data = gsummary.notifications } end if should_include(gsummary.procCount) then res[#res+1] = { name = 'Running Processes', desc = 'Total number of processes that were running during the capture', category = 'general', targetView = 'procs', drillDownKey = '', data = gsummary.procCount } end if(not string.find(filter, 'container') and should_include(gsummary.containerCount)) then res[#res+1] = { name = 'Running Containers', desc = 'Total number of containers that were running during the capture', category = 'general', targetView = 'containers', drillDownKey = '', data = gsummary.containerCount } end if(should_include(gsummary.syscallCount)) then res[#res+1] = { name = 'System Calls', desc = 'Number of system calls performed by any process/container in the system', category = 'general', targetView = 'syscalls', drillDownKey = '', data = gsummary.syscallCount } end if should_include(gsummary.fileBytes) then res[#res+1] = { name = 'File Bytes In+Out', desc = 'Amount of bytes read from or written to the file system', category = 'file', targetView = 'files', drillDownKey = 'fd.directory', targetViewSortingCol = 2, data = gsummary.fileBytes } end if should_include(gsummary.fileBytesR) then res[#res+1] = { name = 'File Bytes In', desc = 'Amount of bytes read from the file system', category = 'file', targetView = 'files', drillDownKey = 'fd.directory', targetViewSortingCol = 0, data = gsummary.fileBytesR } end if should_include(gsummary.fileBytesW) then res[#res+1] = { name = 'File Bytes Out', desc = 'Amount of bytes written to the file system', category = 'file', targetView = 'files', drillDownKey = 'fd.directory', targetViewSortingCol = 1, data = gsummary.fileBytesW } end if should_include(gsummary.fileCount) then res[#res+1] = { name = 'Accessed Files', desc = 'Number of files that have been accessed during the capture', category = 'file', targetView = 'files', targetViewFilter = 'evt.is_io_read=true', drillDownKey = 'fd.directory', targetViewSortingCol = 2, data = gsummary.fileCount } end if should_include(gsummary.fileCountW) then res[#res+1] = { name = 'Modified Files', desc = 'Number of files that have been received writes during the capture', category = 'file', targetView = 'files', drillDownKey = 'fd.directory', targetViewSortingCol = 1, targetViewFilter = 'evt.is_io_write=true', data = gsummary.fileCountW } end if should_include(gsummary.netBytes) then res[#res+1] = { name = 'Net Bytes In+Out', desc = 'Amount of bytes read from or written to the network', category = 'network', targetView = 'sports', drillDownKey = 'fd.directory', targetViewSortingCol = 4, data = gsummary.netBytes } end if should_include(gsummary.netBytesR) then res[#res+1] = { name = 'Net Bytes In', desc = 'Amount of bytes read from the network', category = 'network', targetView = 'sports', drillDownKey = 'fd.sport', targetViewSortingCol = 2, data = gsummary.netBytesR } end if should_include(gsummary.netBytesW) then res[#res+1] = { name = 'Net Bytes Out', desc = 'Amount of bytes written to the network', category = 'network', targetView = 'sports', drillDownKey = 'fd.sport', targetViewSortingCol = 3, data = gsummary.netBytesW } end if should_include(gsummary.connectionCount) then res[#res+1] = { name = 'Active Network Connections', desc = 'Number of network connections that have been accessed during the capture', category = 'network', targetView = 'connections', targetViewFilter = 'evt.is_io=true', drillDownKey = 'fd.sport', targetViewSortingCol = 8, data = gsummary.connectionCount } end if should_include(gsummary.listeningPortCount) then res[#res+1] = { name = 'Listening Ports', desc = 'Number of open ports on this system', category = 'network', targetView = 'port_bindings', drillDownKey = 'fd.sport', data = gsummary.listeningPortCount } end if should_include(gsummary.newListeningPorts) then res[#res+1] = { name = 'New Listening Ports', desc = 'Number of open ports that have been added during the observation interval', category = 'network', targetView = 'port_bindings', drillDownKey = 'fd.sport', data = gsummary.newListeningPorts } end if should_include(gsummary.newConnectionsO) then res[#res+1] = { name = 'New Outbound Connections', desc = 'New client network connections', category = 'network', targetView = 'dig', targetViewTitle = 'Connect events', targetViewFilter = 'evt.type=connect and evt.dir=< and fd.sport exists', drillDownKey = 'NONE', data = gsummary.newConnectionsO } end if should_include(gsummary.newConnectionsI) then res[#res+1] = { name = 'New Inbound Connections', desc = 'New server network connections', category = 'network', targetView = 'dig', targetViewTitle = 'Connect events', targetViewFilter = 'evt.type=accept and evt.dir=< and fd.sport exists', drillDownKey = '', data = gsummary.newConnectionsI } end if should_include(gsummary.executedCommands) then res[#res+1] = { name = 'Executed Commands', desc = 'Number of new programs that have been executed during the observed interval', category = 'security', targetView = 'spy_users_wsysdig', drillDownKey = '', data = gsummary.executedCommands } end if should_include(gsummary.executedInteractiveCommands) then res[#res+1] = { name = 'Executed Interactive Commands', desc = 'Number of new programs that have been executed from a shell during the observed interval', category = 'security', targetView = 'spy_users_wsysdig', targetViewFilter = 'proc.pname=bash', drillDownKey = 'NONE', data = gsummary.executedInteractiveCommands } end if should_include(gsummary.newSymLinksCount) then res[#res+1] = { name = 'New Symlinks', desc = 'Number of new symbolic links that were created', category = 'security', targetView = 'dig', targetViewTitle = 'Symlink creations', targetViewFilter = '(evt.type=symlink or evt.type=symlinkat) and evt.dir=< and evt.failed = false', drillDownKey = 'NONE', data = gsummary.newSymLinksCount } end if should_include(gsummary.sysFileCountW) then res[#res+1] = { name = 'Modified System Files', desc = 'Number of files that have been accessed during the capture', category = 'security', targetViewSortingCol = 1, targetView = 'files', targetViewFilter = 'evt.is_io_write=true', drillDownKey = 'NONE', data = gsummary.sysFileCountW } end if should_include(gsummary.sudoInvocations) then res[#res+1] = { name = 'Sudo Invocations', desc = 'Number of times the sudo program has been called', category = 'security', targetView = 'dig', targetViewTitle = 'Sudo executions', targetViewFilter = 'evt.type=execve and evt.arg.exe=sudo', drillDownKey = 'NONE', data = gsummary.sudoInvocations } end if should_include(gsummary.setnsInvocations) then res[#res+1] = { name = 'Setns Invocations', desc = 'Number of times the setns system call has been called. Setns is typically used to "enter" in another container', category = 'security', targetView = 'dig', targetViewTitle = 'Setns executions', targetViewFilter = 'evt.type=setns', drillDownKey = 'NONE', data = gsummary.setnsInvocations } end if should_include(gsummary.newConnectionsSsh) then res[#res+1] = { name = 'New SSH Connections', desc = 'Client or server connections', category = 'security', targetView = 'dig', targetViewTitle = 'Connect events', targetViewFilter = '(evt.type=accept or evt.type=connect) and evt.dir=< and fd.sport=22', drillDownKey = '', data = gsummary.newConnectionsSsh } end if should_include(gsummary.fileDeletionsCount) then res[#res+1] = { name = 'Deleted Files', desc = 'Number of files that were deleted', category = 'security', targetView = 'dig', targetViewTitle = 'File deletions', targetViewFilter = 'evt.type=unlink or evt.type=unlinkat', drillDownKey = 'NONE', data = gsummary.fileDeletionsCount } end if should_include(gsummary.sysReqCountHttp) then res[#res+1] = { name = 'HTTP Requests', desc = 'Number of HTTP requests', category = 'performance', targetView = 'echo', targetViewTitle = 'HTTP responses', targetViewFilter = '(fd.type=ipv4 or fd.type=ipv6) and evt.buffer contains "HTTP/"', drillDownKey = 'fd.directory', data = gsummary.sysReqCountHttp } end if should_include(gsummary.sysErrCountHttp) then res[#res+1] = { name = 'HTTP Errors', desc = 'Number of HTTP responses with code different from 400', category = 'performance', targetView = 'echo', targetViewTitle = 'HTTP responses', targetViewFilter = '(fd.type=ipv4 or fd.type=ipv6) and evt.arg.data startswith "HTTP/" and not evt.arg.data contains "200"', drillDownKey = 'fd.directory', data = gsummary.sysErrCountHttp } end if should_include(gsummary.openErrorCount) then res[#res+1] = { name = 'File Open Errors', desc = 'Count of failed file opens', category = 'performance', targetView = 'dig', targetViewTitle = 'Failed open() calls', targetViewFilter = 'evt.type=open and evt.rawres<0', drillDownKey = 'fd.directory', data = gsummary.openErrorCount } end if should_include(gsummary.forkCount) then res[#res+1] = { name = 'Fork Count', desc = 'Count of processes and threads that have been created', category = 'performance', targetView = 'dig', targetViewTitle = 'Clone executions', targetViewFilter = 'evt.type=clone and evt.rawres=0', drillDownKey = 'NONE', data = gsummary.forkCount } end if should_include(gsummary.connectErrorCount) then res[#res+1] = { name = 'Failed Connection Attempts', desc = 'Count of failed network connect calls', category = 'performance', targetView = 'dig', targetViewTitle = 'Failed connect() calls', targetViewFilter = 'evt.type=connect and (fd.type=ipv4 or fd.type=ipv6) and evt.rawres<0 and evt.res!=EINPROGRESS', drillDownKey = 'NONE', data = gsummary.connectErrorCount } end if should_include(gsummary.signalCount) then res[#res+1] = { name = 'Received Signals', desc = 'Number of unix signals that have been received by the processes on the system', category = 'performance', targetView = 'dig', targetViewTitle = 'Received signals', targetViewFilter = 'evt.type=signaldeliver', drillDownKey = 'NONE', data = gsummary.signalCount } end if should_include(gsummary.segfaultCount) then res[#res+1] = { name = 'Segmentation Faults', desc = 'Number of process segfaults', category = 'performance', targetView = 'dig', targetViewTitle = 'List of segfault events', targetViewFilter = 'evt.type=signaldeliver and evt.arg.sig=SIGSEV', drillDownKey = 'NONE', data = gsummary.segfaultCount } end if should_include(gsummary.over1msFileIoCount) then res[#res+1] = { name = 'Slow File I/O calls (1ms+)', desc = 'Number of file read or write calls that took more than 1ms to return', category = 'performance', targetView = 'slow_io', targetViewSortingCol = 1, drillDownKey = 'NONE', data = gsummary.over1msFileIoCount } end if should_include(gsummary.over10msFileIoCount) then res[#res+1] = { name = 'Slow File I/O calls (10ms+)', desc = 'Number of file read or write calls that took more than 10ms to return', category = 'performance', targetView = 'slow_io', targetViewSortingCol = 1, drillDownKey = 'NONE', data = gsummary.over10msFileIoCount } end if should_include(gsummary.over100msFileIoCount) then res[#res+1] = { name = 'Slow File I/O calls (100ms+)', desc = 'Number of file read or write calls that took more than 100ms to return', category = 'performance', targetView = 'slow_io', targetViewSortingCol = 1, drillDownKey = 'NONE', data = gsummary.over100msFileIoCount } end if should_include(gsummary.appLogCount) then res[#res+1] = { name = 'App Log Messages', desc = 'Number of wrtites to application log files', category = 'logs', targetView = 'echo', targetViewTitle = 'Application Log Messages', targetViewFilter = '((fd.name contains .log or fd.name contains _log or fd.name contains /var/log) and not (fd.name contains .gz or fd.name contains .tgz)) and evt.is_io_write=true', drillDownKey = 'NONE', data = gsummary.appLogCount } has_cat_logs = true end if should_include(gsummary.appLogCountW) then res[#res+1] = { name = 'App Log Warning Messages', desc = 'Number of writes to application log files containing the word "warning"', category = 'logs', targetView = 'echo', targetViewTitle = 'Warning Application Log Messages', targetViewFilter = '((fd.name contains .log or fd.name contains _log or fd.name contains /var/log) and not (fd.name contains .gz or fd.name icontains .tgz)) and evt.is_io_write=true and evt.arg.data icontains warn', drillDownKey = 'NONE', data = gsummary.appLogCountW } has_cat_logs = true end if should_include(gsummary.appLogCountE) then res[#res+1] = { name = 'App Log Error Messages', desc = 'Number of writes to application log files containing the word "error"', category = 'logs', targetView = 'echo', targetViewTitle = 'Error Application Log Messages', targetViewFilter = '((fd.name contains .log or fd.name contains _log or fd.name contains /var/log) and not (fd.name contains .gz or fd.name contains .tgz)) and evt.is_io_write=true and (evt.arg.data icontains error or evt.arg.data icontains critic or evt.arg.data icontains emergency or evt.arg.data icontains alert)', drillDownKey = 'NONE', data = gsummary.appLogCountE } has_cat_logs = true end if should_include(gsummary.sysLogCount) then res[#res+1] = { name = 'Syslog Messages', desc = 'Number of entries written to syslog', category = 'logs', targetView = 'spy_syslog', targetViewTitle = 'Syslog Messages', drillDownKey = 'NONE', data = gsummary.sysLogCount } has_cat_logs = true end if should_include(gsummary.sysLogCountW) then res[#res+1] = { name = 'Syslog Warning Messages', desc = 'Number of entries with severity WARNING written to syslog', category = 'logs', targetView = 'spy_syslog', targetViewTitle = 'Syslog Messages', targetViewFilter = 'syslog.severity=4', drillDownKey = 'NONE', data = gsummary.sysLogCountW } has_cat_logs = true end if should_include(gsummary.sysLogCountE) then res[#res+1] = { name = 'Syslog Error Messages', desc = 'Number of entries with severity ERROR or lower written to syslog', category = 'logs', targetView = 'spy_syslog', targetViewTitle = 'Syslog Messages', targetViewFilter = 'syslog.severity<4', drillDownKey = 'NONE', data = gsummary.sysLogCountE } has_cat_logs = true end if should_include(gsummary.dockerEvtsCount) then res[#res+1] = { name = 'Docker Events', desc = 'Total number of events generated by docker activity', category = 'infrastructure', targetView = 'docker_events', drillDownKey = 'NONE', data = gsummary.dockerEvtsCount } has_cat_infrastructure = true end -- evaluate dynamic dockerEvtsCount* categories prefix = 'dockerEvtsCount' dockerEvtsCountEvents = {} for ccat in pairs(gsummary) do if starts_with(ccat, prefix) and ccat ~= prefix then if should_include(gsummary[ccat]) then ccat_name = ccat:sub(#prefix + 1) dockerEvtsCountEvents[ccat] = { name = ccat_name .. ' Events', desc = 'Total number of docker events of type ' .. ccat_name, category = 'infrastructure', targetView = 'docker_events', targetViewFilter = 'evt.arg.name="' .. ccat_name .. '"' , drillDownKey = 'NONE', data = gsummary[ccat] } has_cat_infrastructure = true end end end -- sort categories to make sure the final list is "stable" table.sort(dockerEvtsCountEvents, function (a, b) return a.name - b.name end) for i, v in pairs(dockerEvtsCountEvents) do res[#res+1] = v end for i, v in pairs(protocols) do local ccat = 'protoBytes_' .. i if should_include(gsummary[ccat]) then local flt = '' for ii, vv in pairs(v) do flt = flt .. ('fd.sport=' .. vv .. ' or ') end flt = string.sub(flt, 0, #flt - 4) res[#res+1] = { name = i .. ' Bytes', desc = 'Total number of network bytes generated by the ' .. i .. ' protocol', category = 'napps', targetView = 'connections', targetViewFilter = flt, drillDownKey = 'NONE', data = gsummary[ccat] } has_cat_netapps = true end end jtable.info.categories = get_category_table(has_cat_netapps, true, true, has_cat_logs, has_cat_infrastructure) return jtable end function load_index(dirname) local f = io.open(dirname .. '/summary.json', "r") if f == nil then return nil end local res = f:read("*all") f:close() return res end -- Callback called by the engine at the end of the capture function on_capture_end(ts_s, ts_ns, delta) if arg_file_duration == nil then sysdig.run_sysdig('-r "' .. sysdig.get_evtsource_name() .. '" -c wsysdig_summary ' .. arg_n_timeline_samples .. ',' .. delta .. ' ' .. sysdig.get_filter()) return true end local sstr = '' local dirname = sysdig.get_evtsource_name() .. '_wd_index' if file_cache_exists and not disable_index then sstr = load_index(dirname) if sstr == nil then print('{"progress": 100, "error": "can\'t read the trace file index" }') print(']}') return false end jtable = json.decode(sstr) subsample_timelines(jtable) sstr = json.encode(jtable, { indent = true }) else add_summaries(ts_s, ts_ns, gsummary, ssummary) jtable = build_output(delta) sstr = json.encode(jtable, { indent = true }) if not disable_index then os.execute('rm -fr ' .. dirname .. " 2> /dev/null") os.execute('rmdir ' .. dirname .. " 2> nul") os.execute('mkdir ' .. dirname .. " 2> /dev/null") os.execute('md ' .. dirname .. " 2> nul") -- Save the data local f = io.open(dirname .. '/summary.json', "w") if f == nil then print('{"progress": 100, "error": "can\'t create the trace file index" }') print(']}') return false end f:write(sstr) f:close() -- Save the index version local fv = io.open(dirname .. '/VERSION', "w") if fv == nil then print('{"progress": 100, "error": "can\'t create the trace file index" }') print(']}') return false end fv:write(index_format_version) fv:close() end subsample_timelines(jtable) sstr = json.encode(jtable, { indent = true }) end print('{"progress": 100, "data": '.. sstr ..'}') print(']}') return true end sysdig-0.26.4/userspace/sysdig/config_sysdig.h.in000066400000000000000000000013251352731327100220130ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #define SYSDIG_VERSION "${SYSDIG_VERSION}" #define SYSDIG_INSTALLATION_DIR "${CMAKE_INSTALL_PREFIX}" sysdig-0.26.4/userspace/sysdig/csysdig.cpp000066400000000000000000000616551352731327100205730ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include #include "chisel.h" #include "sysdig.h" #include "table.h" #include "utils.h" #ifdef _WIN32 #include "win32/getopt.h" #include #else #include #include #include #endif #include "cursescomponents.h" #include "cursestable.h" #include "cursesui.h" #define MOUSE_CAPABLE_TERM "xterm-1003" #define MOUSE_CAPABLE_TERM_COMPAT "xterm-1002" static bool g_terminate = false; static void usage(); // // Helper functions // static void signal_callback(int signal) { g_terminate = true; } // // Program help // static void usage() { printf( "csysdig version " SYSDIG_VERSION "\n" "Usage: csysdig [options] [filter]\n\n" "Options:\n" " -A, --print-ascii When emitting JSON, only print the text portion of data buffers, and echo\n" " end-of-lines. This is useful to only display human-readable\n" " data.\n" " -B, --bpf=\n" " Enable live capture using the specified BPF probe instead of the kernel module.\n" " The BPF probe can also be specified via the environment variable\n" " SYSDIG_BPF_PROBE. If is left empty, sysdig will\n" " try to load one from the sysdig-probe-loader script.\n" #ifdef HAS_CAPTURE " --cri Path to CRI socket for container metadata\n" " Use the specified socket to fetch data from a CRI-compatible runtime\n" "\n" " --cri-timeout \n" " Wait at most milliseconds for response from CRI\n" #endif " -d , --delay=\n" " Set the delay between updates, in milliseconds. This works\n" " similarly to the -d option in top.\n" " -E, --exclude-users\n" " Don't create the user/group tables by querying the OS when\n" " sysdig starts. This also means that no user or group info\n" " will be written to the tracefile by the -w flag.\n" " The user/group tables are necessary to use filter fields\n" " like user.name or group.name. However, creating them can\n" " increase sysdig's startup time. Moreover, they contain\n" " information that could be privacy sensitive.\n" " --force-term-compat\n" " Try to configure simple terminal settings (xterm-1002) that work\n" " better with terminals like putty. Try to use this flag if you experience\n" " terminal issues like the mouse not working.\n" " -h, --help Print this page\n" " -k , --k8s-api=\n" " Enable Kubernetes support by connecting to the API server\n" " specified as argument. E.g. \"http://admin:password@127.0.0.1:8080\".\n" " The API server can also be specified via the environment variable\n" " SYSDIG_K8S_API.\n" " -K | :[:], --k8s-api-cert= | :[:]\n" " Use the provided files names to authenticate user and (optionally) verify the K8S API\n" " server identity.\n" " Each entry must specify full (absolute, or relative to the current directory) path\n" " to the respective file.\n" " Private key password is optional (needed only if key is password protected).\n" " CA certificate is optional. For all files, only PEM file format is supported. \n" " Specifying CA certificate only is obsoleted - when single entry is provided \n" " for this option, it will be interpreted as the name of a file containing bearer token.\n" " Note that the format of this command-line option prohibits use of files whose names contain\n" " ':' or '#' characters in the file name.\n" " Option can also be provided via the environment variable SYSDIG_K8S_API_CERT.\n" " -l, --list List all the fields that can be used in views.\n" " --large-environment\n" " Support environments larger than 4KiB\n" " When the environment is larger than 4KiB, load the whole\n" " environment from /proc instead of truncating to the first 4KiB\n" " This may fail for short-lived processes and in that case\n" " the truncated environment is used instead.\n" " --logfile=\n" " Print program logs into the given file.\n" " -n , --numevents=\n" " Stop capturing after events\n" " --page-faults Capture user/kernel major/minor page faults\n" " -pc, -pcontainer\n" " Instruct csysdig to use a container-friendly format in its\n" " views.\n" " This will cause several of the views to contain additional\n" " container-related columns.\n" " -R Resolve port numbers to names.\n" " -r , --read=\n" " Read the events from .\n" " --raw Print raw output on a regular terminal instead of enabling\n" " ncurses-based ANSI output.\n" " -s , --snaplen=\n" " Capture the first bytes of each I/O buffer.\n" " By default, the first 80 bytes are captured. Use this\n" " option with caution, it can generate huge trace files.\n" " -T, --force-tracers-capture\n" " Tell the driver to make sure full buffers are captured from\n" " /dev/null, to make sure that tracers are completely\n" " captured. Note that sysdig will enable extended /dev/null\n" " capture by itself after detecting that tracers are written\n" " there, but that could result in the truncation of some\n" " tracers at the beginning of the capture. This option allows\n" " preventing that.\n" " -v , --view=\n" " Run the view with the given ID when csysdig starts.\n" " View IDs can be found in the view documentation pages in\n" " csysdig. Combine this option with a command line filter for\n" " complete output customization.\n" " --version Print version number.\n" " -X, --print-hex-ascii\n" " When emitting JSON, print data buffers in hex and ASCII.\n" "\n" "How to use csysdig:\n" "1. you can either see real time data, or analyze a trace file by using the -r\n" " command line flag.\n" "2. you can switch to a different view by using the F2 key.\n" "3. You can drill down into a selection by typing enter.\n" " You can navigate back by typing backspace.\n" "4. you can observe reads and writes (F5) or see sysdig events (F6) for any\n" " selection.\n" "\nAdditional help can be obtained by clicking F1 while the program is running,\n" "and in the man page.\n\n" ); } #ifdef HAS_CHISELS static void add_chisel_dirs(sinsp* inspector) { // // Add the default chisel directory statically configured by the build system // inspector->add_chisel_dir(SYSDIG_INSTALLATION_DIR CHISELS_INSTALLATION_DIR, false); // // Add the directories configured in the SYSDIG_CHISEL_DIR environment variable // char* s_user_cdirs = getenv("SYSDIG_CHISEL_DIR"); if(s_user_cdirs != NULL) { vector user_cdirs = sinsp_split(s_user_cdirs, ';'); for(uint32_t j = 0; j < user_cdirs.size(); j++) { inspector->add_chisel_dir(user_cdirs[j], true); } } } static void print_views(sinsp_view_manager* view_manager) { Json::FastWriter writer; Json::Value root; vector* vlist = view_manager->get_views(); for(auto it = vlist->begin(); it != vlist->end(); ++it) { Json::Value jv; sinsp_view_info& vinfo = *it; jv["id"] = vinfo.m_id; jv["name"] = vinfo.m_name; jv["description"] = vinfo.m_description; jv["isRoot"] = vinfo.m_is_root; jv["drilldownTarget"] = vinfo.m_drilldown_target; jv["filter"] = vinfo.m_filter; jv["canDrillDown"] = (vinfo.m_type == sinsp_view_info::T_TABLE); for(auto it = vinfo.m_applies_to.begin(); it != vinfo.m_applies_to.end(); ++it) { jv["appliesTo"].append(*it); } for(auto it = vinfo.m_tags.begin(); it != vinfo.m_tags.end(); ++it) { jv["tags"].append(*it); } for(auto it = vinfo.m_tips.begin(); it != vinfo.m_tips.end(); ++it) { jv["tips"].append(*it); } root.append(jv); } string ouput = writer.write(root); printf("%s", ouput.substr(0, ouput.size() - 1).c_str()); } #endif captureinfo do_inspect(sinsp* inspector, uint64_t cnt, sinsp_cursesui* ui) { captureinfo retval; int32_t res; sinsp_evt* ev; // // Loop through the events // while(1) { if(retval.m_nevts == cnt || g_terminate) { // // End of capture, either because the user stopped it, or because // we reached the event count specified with -n. // break; } res = inspector->next(&ev); if(res == SCAP_TIMEOUT) { continue; } else if(res != SCAP_EOF && res != SCAP_SUCCESS) { // // Event read error. // Notify the chisels that we're exiting, and then die with an error. // if(inspector->is_live()) { throw sinsp_exception(inspector->getlasterr()); } else { ui->set_truncated_input(true); res = SCAP_EOF; } } if(ui->process_event(ev, res) == true) { return retval; } retval.m_nevts++; } return retval; } string g_version_string = SYSDIG_VERSION; sysdig_init_res csysdig_init(int argc, char **argv) { sysdig_init_res res; sinsp* inspector = NULL; vector infiles; int op; uint64_t cnt = -1; uint32_t snaplen = 0; int long_index = 0; int32_t n_filterargs = 0; captureinfo cinfo; string errorstr; string display_view; bool print_containers = false; uint64_t refresh_interval_ns = 2000000000; bool list_flds = false; bool is_interactive = false; int32_t json_first_row = 0; int32_t json_last_row = 0; int32_t sorting_col = -1; bool list_views = false; bool bpf = false; string bpf_probe; #ifdef HAS_CAPTURE string cri_socket_path; #endif #ifndef _WIN32 sinsp_table::output_type output_type = sinsp_table::OT_CURSES; #else sinsp_table::output_type output_type = sinsp_table::OT_JSON; #endif string* k8s_api = 0; string* k8s_api_cert = 0; string* mesos_api = 0; bool terminal_with_mouse = false; bool force_tracers_capture = false; bool force_term_compat = false; sinsp_evt::param_fmt event_buffer_format = sinsp_evt::PF_NORMAL; bool page_faults = false; static struct option long_options[] = { {"print-ascii", no_argument, 0, 'A' }, {"bpf", optional_argument, 0, 'B' }, #ifdef HAS_CAPTURE {"cri", required_argument, 0, 0 }, {"cri-timeout", required_argument, 0, 0 }, #endif {"delay", required_argument, 0, 'd' }, {"exclude-users", no_argument, 0, 'E' }, {"from", required_argument, 0, 0 }, {"help", no_argument, 0, 'h' }, {"k8s-api", required_argument, 0, 'k'}, {"k8s-api-cert", required_argument, 0, 'K' }, {"json", no_argument, 0, 'j' }, {"interactive", optional_argument, 0, 0 }, {"large-environment", no_argument, 0, 0 }, {"list", optional_argument, 0, 'l' }, {"list-views", no_argument, 0, 0}, {"mesos-api", required_argument, 0, 'm'}, {"numevents", required_argument, 0, 'n' }, {"page-faults", no_argument, 0, 0 }, {"print", required_argument, 0, 'p' }, {"resolve-ports", no_argument, 0, 'R'}, {"readfile", required_argument, 0, 'r' }, {"raw", no_argument, 0, 0 }, {"snaplen", required_argument, 0, 's' }, {"logfile", required_argument, 0, 0 }, {"force-tracers-capture", required_argument, 0, 'T'}, {"force-term-compat", no_argument, 0, 0}, {"sortingcol", required_argument, 0, 0 }, {"to", required_argument, 0, 0 }, {"view", required_argument, 0, 'v' }, {"version", no_argument, 0, 0 }, {"print-hex-ascii", no_argument, 0, 'X'}, {0, 0, 0, 0} }; // // Parse the arguments // try { inspector = new sinsp(); #ifdef HAS_CHISELS add_chisel_dirs(inspector); #endif // // Parse the args // while((op = getopt_long(argc, argv, "AB::d:Ehk:K:jlm:n:p:Rr:s:Tv:X", long_options, &long_index)) != -1) { switch(op) { case '?': // // Command line error // throw sinsp_exception("command line error"); break; case 'A': if(event_buffer_format != sinsp_evt::PF_NORMAL) { fprintf(stderr, "you cannot specify more than one output format\n"); delete inspector; return sysdig_init_res(EXIT_SUCCESS); } event_buffer_format = sinsp_evt::PF_EOLS_COMPACT; break; case 'B': { bpf = true; if(optarg) { bpf_probe = optarg; } break; } case 'd': try { refresh_interval_ns = sinsp_numparser::parseu64(optarg) * 1000000; } catch(...) { throw sinsp_exception("can't parse the -d argument, make sure it's a number"); } if(refresh_interval_ns < 100000000) { throw sinsp_exception("Period must be bigger then 100ms"); } break; case 'E': inspector->set_import_users(false); break; case 'h': usage(); delete inspector; return sysdig_init_res(EXIT_SUCCESS); case 'k': k8s_api = new string(optarg); break; case 'K': k8s_api_cert = new string(optarg); break; case 'j': output_type = sinsp_table::OT_JSON; break; case 'l': list_flds = true; break; case 'm': mesos_api = new string(optarg); break; case 'n': try { cnt = sinsp_numparser::parseu64(optarg); } catch(...) { throw sinsp_exception("can't parse the -n argument, make sure it's a number"); } if(cnt <= 0) { throw sinsp_exception(string("invalid event count ") + optarg); res.m_res = EXIT_FAILURE; goto exit; } break; case 'p': if(string(optarg) == "c" || string(optarg) == "container") { inspector->set_print_container_data(true); print_containers = true; } break; case 'R': inspector->set_hostname_and_port_resolution_mode(true); break; case 'r': infiles.push_back(optarg); k8s_api = new string(); mesos_api = new string(); break; case 's': snaplen = atoi(optarg); break; case 'T': force_tracers_capture = true; break; case 'v': display_view = optarg; break; case 'X': if(event_buffer_format != sinsp_evt::PF_NORMAL) { fprintf(stderr, "you cannot specify more than one output format\n"); delete inspector; return sysdig_init_res(EXIT_FAILURE); } event_buffer_format = sinsp_evt::PF_HEXASCII; break; case 0: { if(long_options[long_index].flag != 0) { break; } string optname = string(long_options[long_index].name); if(optname == "version") { printf("sysdig version %s\n", SYSDIG_VERSION); delete inspector; return sysdig_init_res(EXIT_SUCCESS); } else if(optname == "interactive") { is_interactive = true; output_type = sinsp_table::OT_JSON; } else if(optname == "large-environment") { inspector->set_large_envs(true); } #ifdef HAS_CAPTURE else if(optname == "cri") { cri_socket_path = optarg; } else if(optname == "cri-timeout") { inspector->set_cri_timeout(sinsp_numparser::parsed64(optarg)); } #endif else if(optname == "logfile") { inspector->set_log_file(optarg); } else if(optname == "raw") { output_type = sinsp_table::OT_RAW; } else if(optname == "force-term-compat") { force_term_compat = true; } else if(optname == "from") { json_first_row = sinsp_numparser::parsed32(optarg); } else if(optname == "to") { json_last_row = sinsp_numparser::parsed32(optarg); } else if(optname == "sortingcol") { sorting_col = sinsp_numparser::parsed32(optarg); } else if(optname == "list-views") { list_views = true; } else if(optname == "page-faults") { page_faults = true; } } break; default: break; } } #ifdef HAS_CAPTURE if(!cri_socket_path.empty()) { inspector->set_cri_socket_path(cri_socket_path); } #endif string filter; // // If -l was specified, print the fields and exit // if(list_flds) { list_fields(false, false); res.m_res = EXIT_SUCCESS; goto exit; } // // the filter is at the end of the command line // if(optind + n_filterargs < argc) { #ifdef HAS_FILTERING for(int32_t j = optind + n_filterargs; j < argc; j++) { filter += argv[j]; if(j < argc) { filter += " "; } } #else fprintf(stderr, "filtering not compiled.\n"); res.m_res = EXIT_FAILURE; goto exit; #endif } if(!bpf) { const char *probe = scap_get_bpf_probe_from_env(); if(probe) { bpf = true; bpf_probe = probe; } } if(bpf) { inspector->set_bpf_probe(bpf_probe); } if(signal(SIGINT, signal_callback) == SIG_ERR) { fprintf(stderr, "An error occurred while setting SIGINT signal handler.\n"); res.m_res = EXIT_FAILURE; goto exit; } if(signal(SIGTERM, signal_callback) == SIG_ERR) { fprintf(stderr, "An error occurred while setting SIGTERM signal handler.\n"); res.m_res = EXIT_FAILURE; goto exit; } if(json_last_row < json_first_row) { fprintf(stderr, "'to' argument cannot be smaller than the 'from' one.\n"); res.m_res = EXIT_FAILURE; goto exit; } // // Initialize ncurses // #ifndef NOCURSESUI if(output_type == sinsp_table::OT_CURSES) { // // Check if terminal has mouse support // const char* mct = force_term_compat? MOUSE_CAPABLE_TERM_COMPAT : MOUSE_CAPABLE_TERM; terminal_with_mouse = (tgetent(NULL, mct) != 0); if(terminal_with_mouse) { // // Enable fine-grained mouse activity capture by setting xterm-1002 // setenv("TERM", mct, 1); } (void) initscr(); // initialize the curses library (void) nonl(); // tell curses not to do NL->CR/NL on output intrflush(stdscr, false); keypad(stdscr, true); curs_set(0); if(has_colors()) { start_color(); } use_default_colors(); mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL); noecho(); timeout(0); // If this is uncommented, it's possible to natively handle stuff like CTRL+c //raw(); } #endif // // Create the list of views // sinsp_view_manager view_manager; // // Scan the chisel list to load the Lua views, and add them to the list // vector chlist; sinsp_chisel::get_chisel_list(&chlist); for(auto it : chlist) { if(it.m_viewinfo.m_valid) { if(print_containers) { it.m_viewinfo.apply_tag("containers"); } else { it.m_viewinfo.apply_tag("default"); } if(it.m_viewinfo.m_tags.size() != 0) { if(it.m_viewinfo.m_tags[0] == "Containers") { continue; } } if(output_type != sinsp_table::OT_JSON) { if(std::find(it.m_viewinfo.m_tags.begin(), it.m_viewinfo.m_tags.end(), "nocsysdig") != it.m_viewinfo.m_tags.end()) { continue; } } view_manager.add(&it.m_viewinfo); } } // // Set the initial display view // view_manager.set_selected_view(display_view); if(list_views) { print_views(&view_manager); goto exit; } // // Go through the input sources and apply the processing to all of them // for(uint32_t j = 0; j < infiles.size() || infiles.size() == 0; j++) { // // Initialize the UI // sinsp_cursesui ui(inspector, (infiles.size() != 0)? infiles[0] : "", (filter.size() != 0)? filter : "", refresh_interval_ns, print_containers, output_type, terminal_with_mouse, json_first_row, json_last_row, sorting_col, event_buffer_format); ui.configure(&view_manager); if(display_view == "dig" || display_view == "echo") { ui.start(false, true); } else { ui.start(false, false); } if(is_interactive) { printf("ready\n"); // // In interactive mode, make sure stderr is flushed at every printf // setbuf(stderr, NULL); // // Set the UI in interactive mode and start listening to user // input. // ui.set_interactive(true); } // // Launch the capture // if(infiles.size() != 0) { // // We have a file to open // inspector->open(infiles[j]); } else { if(j > 0) { break; } // // No file to open, this is a live capture // #if defined(HAS_CAPTURE) bool open_success = true; try { inspector->open(""); } catch(sinsp_exception e) { open_success = false; } // // Starting the live capture failed, try to load the driver with // modprobe. // if(!open_success) { open_success = true; if(bpf) { if(bpf_probe.empty()) { if(system("sysdig-probe-loader bpf")) { fprintf(stderr, "Unable to load the BPF probe\n"); } } } else { if(system("modprobe " PROBE_NAME " > /dev/null 2> /dev/null")) { fprintf(stderr, "Unable to load the driver\n"); } } inspector->open(""); } #else // // Starting live capture // If this fails on Windows and OSX, don't try with any driver // inspector->open(""); #endif // // Enable gathering the CPU from the kernel module // inspector->set_get_procs_cpu_from_driver(true); } // // If required, set the snaplen // if(snaplen != 0) { inspector->set_snaplen(snaplen); } // // If required, tell the driver to enable tracers capture // if(force_tracers_capture) { inspector->enable_tracers_capture(); } if(page_faults) { inspector->enable_page_faults(); } // // run k8s, if required // if(k8s_api) { if(!k8s_api_cert) { if(char* k8s_cert_env = getenv("SYSDIG_K8S_API_CERT")) { k8s_api_cert = new string(k8s_cert_env); } } inspector->init_k8s_client(k8s_api, k8s_api_cert); k8s_api = 0; k8s_api_cert = 0; } else if(char* k8s_api_env = getenv("SYSDIG_K8S_API")) { if(k8s_api_env != NULL) { if(!k8s_api_cert) { if(char* k8s_cert_env = getenv("SYSDIG_K8S_API_CERT")) { k8s_api_cert = new string(k8s_cert_env); } } k8s_api = new string(k8s_api_env); inspector->init_k8s_client(k8s_api, k8s_api_cert); } else { delete k8s_api; delete k8s_api_cert; } k8s_api = 0; k8s_api_cert = 0; } // // run mesos, if required // if(mesos_api) { inspector->init_mesos_client(mesos_api); } else if(char* mesos_api_env = getenv("SYSDIG_MESOS_API")) { if(mesos_api_env != NULL) { mesos_api = new string(mesos_api_env); inspector->init_mesos_client(mesos_api); } } delete mesos_api; mesos_api = 0; if(output_type == sinsp_table::OT_JSON) { printf("{\"slices\": [\n"); if(display_view != "dig" && display_view != "echo") { printf("{\"progress\": 0},\n"); } } // // Start the capture loop // cinfo = do_inspect(inspector, cnt, &ui); if(output_type == sinsp_table::OT_JSON) { printf("]}\n"); //printf("%c", EOF); } // // Done. Close the capture. // inspector->close(); } } catch(sinsp_capture_interrupt_exception&) { } catch(sinsp_exception& e) { errorstr = e.what(); res.m_res = e.scap_rc(); } catch(std::exception& e) { errorstr = e.what(); res.m_res = EXIT_FAILURE; } catch(...) { errorstr = "uncaught exception"; res.m_res = EXIT_FAILURE; } exit: if(inspector) { delete inspector; } // // Restore the original screen // #ifndef NOCURSESUI if(output_type == sinsp_table::OT_CURSES) { endwin(); } #endif if(errorstr != "") { cerr << errorstr << endl; } return res; } // // MAIN // int main(int argc, char **argv) { sysdig_init_res res; // // Run csysdig // res = csysdig_init(argc, argv); #ifdef _WIN32 _CrtDumpMemoryLeaks(); #endif return res.m_res; } sysdig-0.26.4/userspace/sysdig/fields_info.cpp000066400000000000000000000144711352731327100214010ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ // // Various helper functions to render stuff on the screen // #define __STDC_FORMAT_MACROS #include #include #include #include #include #include "chisel.h" #include "sysdig.h" // 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) void list_fields(bool verbose, bool markdown, bool names_only) { uint32_t j, l, m; int32_t k; if(markdown && !names_only) { printf("# Sysdig Filter Fields List\n\n"); } vector fc_plugins; sinsp::get_filtercheck_fields_info(&fc_plugins); for(j = 0; j < fc_plugins.size(); j++) { const filter_check_info* fci = fc_plugins[j]; if(fci->m_flags & filter_check_info::FL_HIDDEN) { continue; } if(!names_only) { if(markdown) { printf("## Filter Class: %s\n\n", fci->m_name.c_str()); } else { printf("\n----------------------\n"); printf("Field Class: %s\n\n", fci->m_name.c_str()); } } for(k = 0; k < fci->m_nfields; k++) { const filtercheck_field_info* fld = &fci->m_fields[k]; if(fld->m_flags & EPF_TABLE_ONLY) { continue; } if(names_only) { printf("%s\n", fld->m_name); } else if(markdown) { printf("**Name**: %s \n", fld->m_name); printf("**Description**: %s \n", fld->m_description); printf("**Type**: %s \n\n", param_type_to_string(fld->m_type)); } else { printf("%s", fld->m_name); uint32_t namelen = (uint32_t)strlen(fld->m_name); if(namelen >= DESCRIPTION_TEXT_START) { printf("\n"); namelen = 0; } for(l = 0; l < DESCRIPTION_TEXT_START - namelen; l++) { printf(" "); } string desc(fld->m_description); if(fld->m_flags & EPF_FILTER_ONLY) { desc = "(FILTER ONLY) " + desc; } if(verbose) { desc += string(" Type:") + param_type_to_string(fld->m_type) + "."; } size_t desclen = desc.size(); for(l = 0; l < desclen; l++) { if(l % (CONSOLE_LINE_LEN - DESCRIPTION_TEXT_START) == 0 && l != 0) { printf("\n"); for(m = 0; m < DESCRIPTION_TEXT_START; m++) { printf(" "); } } printf("%c", desc[l]); } printf("\n"); } } } } void list_events(sinsp* inspector) { uint32_t j, k; string tstr; sinsp_evttables* einfo = inspector->get_event_info_tables(); const struct ppm_event_info* etable = einfo->m_event_info; for(j = 0; j < PPM_EVENT_MAX; j++) { const struct ppm_event_info ei = etable[j]; char dir = (PPME_IS_ENTER(j))? '>' : '<'; if((ei.flags & EF_UNUSED) || (ei.flags & EF_OLD_VERSION) || (ei.category & EC_INTERNAL)) { continue; } printf("%c %s(", dir, ei.name); for(k = 0; k < ei.nparams; k++) { if(k != 0) { printf(", "); } printf("%s %s", param_type_to_string(ei.params[k].type), ei.params[k].name); } printf(")\n"); } } #ifdef HAS_CHISELS 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 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"); } } #endif // HAS_CHISELS sysdig-0.26.4/userspace/sysdig/man/000077500000000000000000000000001352731327100171605ustar00rootroot00000000000000sysdig-0.26.4/userspace/sysdig/man/CMakeLists.txt000066400000000000000000000025161352731327100217240ustar00rootroot00000000000000# # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # find_program(PANDOC pandoc) if (PANDOC) add_custom_target(man_sysdig ALL COMMAND ${PANDOC} -s -f markdown_github -t man sysdig.md -o ${CMAKE_CURRENT_BINARY_DIR}/sysdig.8 WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" VERBATIM) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/sysdig.8 DESTINATION share/man/man8) add_custom_target(man_csysdig ALL COMMAND ${PANDOC} -s -f markdown_github -t man csysdig.md -o ${CMAKE_CURRENT_BINARY_DIR}/csysdig.8 WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" VERBATIM) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/csysdig.8 DESTINATION share/man/man8) else() install(FILES sysdig.8 DESTINATION share/man/man8) install(FILES csysdig.8 DESTINATION share/man/man8) endif() sysdig-0.26.4/userspace/sysdig/man/build.sh000077500000000000000000000013571352731327100206240ustar00rootroot00000000000000# # Copyright (C) 2013-2018 Draios Inc dba Sysdig. # # This file is part of sysdig . # # 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. # pandoc -s -f markdown_github -t man sysdig.md -o sysdig.8 pandoc -s -f markdown_github -t man csysdig.md -o csysdig.8 sysdig-0.26.4/userspace/sysdig/man/csysdig.8000066400000000000000000000307201352731327100207200ustar00rootroot00000000000000.\" Automatically generated by Pandoc 1.19.2.1 .\" .TH "" "" "" "" "" .hy .SS NAME .PP csysdig \- the ncurses user interface for sysdig .SS SYNOPSIS .PP \f[B]csysdig\f[] [\f[I]option\f[]]... [\f[I]filter\f[]] .SS DESCRIPTION .PP csysdig exports sysdig\[aq]s functionality through an intuitive and powerful ncurses\-based user interface. .PP csysdig has been designed to mimic tools like \f[B]top\f[] and \f[B]htop\f[], but it offers richer functionality, including: .IP \[bu] 2 Support for both live analysis and sysdig trace files. Trace files can come from the same machine or from another machine. .IP \[bu] 2 Visibility into a broad range of metrics, including CPU, memory, disk I/O, network I/O. .IP \[bu] 2 Ability to observe input/output activity for processes, files, network connections and more. .IP \[bu] 2 Ability to drill down into processes, files, network connections and more to further explore their behavior. .IP \[bu] 2 Full customization support. .IP \[bu] 2 Support for sysdig\[aq]s filtering language. .IP \[bu] 2 Container support by design. .PP csysdig works on any terminal, and has support for colors and mouse input. .PP \f[B]Views\f[] .PP csysdig is based on the concept of \[aq]views\[aq], little Lua scripts that determine how metrics are collected, processed and represented on screen. Including a new visualization to csysdig doesn\[aq]t require to update the program, and is simply a matter of adding a new view. Views rely on the sysdig processing engine, and this means that they can include any sysdig filter field. Views are located in the sysdig chisel directory path, usually \f[I]/usr/share/sysdig/chisels\f[] and \f[I]~/.chisels\f[]. .SS BASIC USAGE .PP Here are some basic tips to get you started with sysdig: .IP "1." 3 If you run csysdig without arguments, it will display live system data, updating every 2 seconds. To analyze a trace file, use the \-r command line flag. .IP "2." 3 You can switch to a different view by using the \f[I]F2\f[] key. .IP "3." 3 You can drill down into a selection by clicking \f[I]enter\f[]. You can navigate back by typing \f[I]backspace\f[]. .IP "4." 3 You can observe input/output for the currently selected entity by typing \f[I]F5\f[] .IP "5." 3 You can see sysdig events for the currently selected entity by typing \f[I]F6\f[] .SS DRILLING DOWN .PP You drill down by selecting an element in a view and then clicking \f[I]enter\f[]. Once inside a selection, you can switch to a different view, and the new view will be applied in the context of the selection. For example, if you drill down into a process called foo and then switch to the \f[I]Connections\f[] view, the output will include only the connections made or received by \f[I]foo\f[]. .PP To drill down multiple times, keep clicking \f[I]enter\f[]. For example, you can click on a container in the \f[I]Containers\f[] view to get the processes running inside it, and then click on one of the processes to see its threads. .SS ACTIONS AND HOTKEYS .PP Each view has a list of command lines that can be executed in the context of the current selection by pressing \[aq]hotkeys\[aq]. For example, pressing \[aq]k\[aq] in the Processes view kills the selected process, pressing \[aq]b\[aq] in the Containers view opens a bash shell in the selected container. .PP Each view supports different actions. You can see which actions a view supports by pressing F8. You can customize the view\[aq]s actions by editing the view\[aq]s Lua file. .SS CONTAINERS SUPPORT .PP Starting csysdig with the \-pc command line switch will cause many of the views to include additional container information. For example, the \f[I]Processes\f[] will include a column showing the container the process belongs to. Similarly, the \f[I]Connections\f[] view will show which container each connection belongs to. .SS INTERACTIVE COMMANDS .SS Views Window .PP \f[B]Arrows, PgUP, PgDn, Home, End\f[] .PD 0 .P .PD Change the selection and scroll view content, both vertically and horizontally. .PP \f[B]Enter\f[] .PD 0 .P .PD Drill down into the currently highlighted entry. .PP \f[B]Backspace\f[] .PD 0 .P .PD Navigate back to the previous view. .PP \f[B]F2\f[] .PD 0 .P .PD Show the view picker. This will let you switch to another view. .PP \f[B]CTRL+F /\f[] .PD 0 .P .PD Incremental search in the list of view entries. .PP \f[B]F4\f[] .PD 0 .P .PD Incremental filtering of the view entries. .PP \f[B]F5, e\f[] .PD 0 .P .PD \[aq]echo FDs\[aq] for the selection, i.e. view FD input/output for the currently highlighted entry. .PP \f[B]F6, d\f[] .PD 0 .P .PD \[aq]dig\[aq] into the selection, i.e. view sysdig events for the currently highlighted entry. Refer to the sysdig man page to learn about interpreting the content of this window. .PP \f[B]F7\f[] .PD 0 .P .PD Show the help page for the currently displayed view. .PP \f[B]F8\f[] .PD 0 .P .PD Open the view\[aq]s actions panel. .PP \f[B]F9, >\f[] .PD 0 .P .PD Open the column sort panel. .PP \f[B]F10, q\f[] .PD 0 .P .PD Quit. .PP \f[B]DEL, c\f[] .PD 0 .P .PD For views that are listing elements without aggregating them by key (identifiable by yellow column headers), this command clears the view content. .PP \f[B]p\f[] .PD 0 .P .PD Pause screen updates. .PP \f[B]\f[C]\ <1\-9>\f[]\f[] .PD 0 .P .PD sort column \f[C]\f[] .PP \f[B]F1, h, ?\f[] .PD 0 .P .PD Show the help screen. .SS Echo and sysdig Windows .PP \f[B]Arrows, PgUP, PgDn, Home, End\f[] .PD 0 .P .PD Scroll the page content. .PP \f[B]Backspace\f[] .PD 0 .P .PD Navigate back to the previous view. .PP \f[B]CTRL+F /\f[] .PD 0 .P .PD Search inside the window content. .PP \f[B]F3\f[] .PD 0 .P .PD Find Next. .PP \f[B]F2\f[] .PD 0 .P .PD Chose the output rendering format. Options are \[aq]Dotted ASCII\[aq] (non\-printable binary bytes are rendered as dots), \[aq]Printable ASCII\[aq] (non\-printable binary bytes are not included and line endings are rendered accurately) and \[aq]Hex\[aq] (dotted ASCII representation is included together with the Hexadecimal rendering of the buffers). .PP \f[B]DEL, c\f[] .PD 0 .P .PD Clear the screen content. .PP \f[B]p\f[] .PD 0 .P .PD Pause screen updates. .PP \f[B]CTRL+G\f[] .PD 0 .P .PD Go to line. .SS Spectrogram Window .PP \f[B]F2\f[] .PD 0 .P .PD Show the view picker. This will let you switch to another view. .PP \f[B]p\f[] .PD 0 .P .PD Pause/Resume the visualization. .PP \f[B]Backspace\f[] .PD 0 .P .PD Navigate back to the previous view. .SS MOUSE USAGE .IP \[bu] 2 Clicking on column headers lets you sort the table. .IP \[bu] 2 Double clicking on row entries performs a drill down. .IP \[bu] 2 Clicking on the filter string at the top of the screen (the text after \[aq]Filter:\[aq]) lets you change the sysdig filter and customize the view content. .IP \[bu] 2 You can use the mouse on the entries in the menu at the bottom of the screen to perform their respective actions. .SS COMMAND LINE OPTIONS .PP \f[B]\-d\f[] \f[I]period\f[], \f[B]\-\-delay\f[]=\f[I]period\f[] .PD 0 .P .PD Set the delay between updates, in milliseconds (by default = 2000). This works similarly to the \-d option in top. .PP \f[B]\-E\f[], \f[B]\-\-exclude\-users\f[] .PD 0 .P .PD Don\[aq]t create the user/group tables by querying the OS when sysdig starts. This also means that no user or group info will be written to the tracefile by the \-w flag. The user/group tables are necessary to use filter fields like user.name or group.name. However, creating them can increase sysdig\[aq]s startup time. .PP \f[B]\-\-force\-term\-compat\f[] .PD 0 .P .PD Try to configure simple terminal settings (xterm\-1002) that work better with terminals like putty. Try to use this flag if you experience terminal issues like the mouse not working. .PP \f[B]\-h\f[], \f[B]\-\-help\f[] .PD 0 .P .PD Print this page .PP \f[B]\-k\f[], \f[B]\-\-k8s\-api\f[] .PD 0 .P .PD Enable Kubernetes support by connecting to the API server specified as argument. E.g. "". The API server can also be specified via the environment variable SYSDIG_K8S_API. .PP \f[B]\-K\f[] \f[I]btfile | certfile:keyfile[#password][:cacertfile]\f[], \f[B]\-\-k8s\-api\-cert=\f[]\f[I]btfile | certfile:keyfile[#password][:cacertfile]\f[] .PD 0 .P .PD Use the provided files names to authenticate user and (optionally) verify the K8S API server identity. Each entry must specify full (absolute, or relative to the current directory) path to the respective file. Private key password is optional (needed only if key is password protected). CA certificate is optional. For all files, only PEM file format is supported. Specifying CA certificate only is obsoleted \- when single entry is provided for this option, it will be interpreted as the name of a file containing bearer token. Note that the format of this command\-line option prohibits use of files whose names contain \[aq]:\[aq] or \[aq]#\[aq] characters in the file name. Option can also be provided via the environment variable SYSDIG_K8S_API_CERT. .PP \f[B]\-l\f[], \f[B]\-\-list\f[] .PD 0 .P .PD List all the fields that can be used in views. .PP \f[B]\-\-logfile\f[] \f[I]file\f[] .PD 0 .P .PD Print program logs into the given file. .PP \f[B]\-m\f[] \f[I]url[,marathon\-url]\f[], \f[B]\-\-mesos\-api=\f[]\f[I]url[,marathon\-url]\f[] .PD 0 .P .PD Enable Mesos support by connecting to the API server specified as argument (e.g. ). Mesos url is required. Marathon url is optional, defaulting to auto\-follow \- if Marathon API server is not provided, csysdig will attempt to retrieve (and subsequently follow, if it migrates) the location of Marathon API server from the Mesos master. Note that, with auto\-follow, csysdig will likely receive a cluster internal IP address for Marathon API server, so running csysdig with Marathon auto\-follow from a node that is not part of Mesos cluster may not work. Additionally, running csysdig with Mesos support on a node that has no containers managed by Mesos is of limited use because, although cluster metadata will be collected, there will be no Mesos/Marathon filtering capability. The API servers can also be specified via the environment variable SYSDIG_MESOS_API. .PP \f[B]\-n\f[] \f[I]num\f[], \f[B]\-\-numevents\f[]=\f[I]num\f[] .PD 0 .P .PD Stop capturing after \f[I]num\f[] events .PP \f[B]\-\-page\-faults\f[] .PD 0 .P .PD Capture user/kernel major/minor page faults .PP \f[B]\-pc\f[], \f[B]\-pcontainers\f[]_ .PD 0 .P .PD Instruct csysdig to use a container\-friendly format in its views. This will cause several of the views to contain additional container\-related columns. .PP \f[B]\-R\f[], \f[B]\-\-resolve\-ports\f[] .PD 0 .P .PD Resolve port numbers to names. .PP \f[B]\-r\f[] \f[I]readfile\f[], \f[B]\-\-read\f[]=\f[I]readfile\f[] .PD 0 .P .PD Read the events from \f[I]readfile\f[]. .PP \f[B]\-s\f[] \f[I]len\f[], \f[B]\-\-snaplen\f[]=\f[I]len\f[] .PD 0 .P .PD Capture the first \f[I]len\f[] bytes of each I/O buffer. By default, the first 80 bytes are captured. Use this option with caution, it can generate huge trace files. .PP \f[B]\-T\f[], \f[B]\-\-force\-tracers\-capture\f[] .PD 0 .P .PD Tell the driver to make sure full buffers are captured from /dev/null, to make sure that tracers are completely captured. Note that sysdig will enable extended /dev/null capture by itself after detecting that tracers are written there, but that could result in the truncation of some tracers at the beginning of the capture. This option allows preventing that. .PP \f[B]\-v\f[] \f[I]view_id\f[], \f[B]\-\-views\f[]=\f[I]view_id\f[] .PD 0 .P .PD Run the view with the given ID when csysdig starts. View IDs can be found in the view documentation pages in csysdig. Combine this option with a command line filter for complete output customization. .PP \f[B]\-\-version\f[] .PD 0 .P .PD Print version number. .SS FILTERING .PP Similarly to what you do with sysdig, you can specify a filter on the command line to restrict the events that csysdig processes. To modify the filter while the program is running, or to add a filter at runtime, click on the filter text in the UI with the mouse. .SS CUSTOMIZING CSYSDIG .PP csysdig is completely customizable. This means that you can modify any of the csysdig views, and even create your own views. Like sysdig chisels, csysdig views are Lua scripts. Full information can be found at the following github wiki page: . .SS FILES .PP \f[I]/usr/share/sysdig/chisels\f[] .PD 0 .P .PD The global views directory. .PP \f[I]~/.chisels\f[] .PD 0 .P .PD The personal views directory. .SS AUTHOR .PP Draios Inc. (dba Sysdig) .SS SEE ALSO .PP \f[B]sysdig\f[](8), \f[B]strace\f[](8), \f[B]tcpdump\f[](8), \f[B]lsof\f[](8) sysdig-0.26.4/userspace/sysdig/man/csysdig.md000066400000000000000000000260731352731327100211570ustar00rootroot00000000000000NAME ---- csysdig - the ncurses user interface for sysdig SYNOPSIS -------- **csysdig** [*option*]... [*filter*] DESCRIPTION ----------- csysdig exports sysdig's functionality through an intuitive and powerful ncurses-based user interface. csysdig has been designed to mimic tools like **top** and **htop**, but it offers richer functionality, including: - Support for both live analysis and sysdig trace files. Trace files can come from the same machine or from another machine. - Visibility into a broad range of metrics, including CPU, memory, disk I/O, network I/O. - Ability to observe input/output activity for processes, files, network connections and more. - Ability to drill down into processes, files, network connections and more to further explore their behavior. - Full customization support. - Support for sysdig's filtering language. - Container support by design. csysdig works on any terminal, and has support for colors and mouse input. **Views** csysdig is based on the concept of 'views', little Lua scripts that determine how metrics are collected, processed and represented on screen. Including a new visualization to csysdig doesn't require to update the program, and is simply a matter of adding a new view. Views rely on the sysdig processing engine, and this means that they can include any sysdig filter field. Views are located in the sysdig chisel directory path, usually */usr/share/sysdig/chisels* and *~/.chisels*. BASIC USAGE ----------- Here are some basic tips to get you started with sysdig: 1. If you run csysdig without arguments, it will display live system data, updating every 2 seconds. To analyze a trace file, use the -r command line flag. 2. You can switch to a different view by using the _F2_ key. 3. You can drill down into a selection by clicking _enter_. You can navigate back by typing _backspace_. 4. You can observe input/output for the currently selected entity by typing _F5_ 5. You can see sysdig events for the currently selected entity by typing _F6_ DRILLING DOWN ------------- You drill down by selecting an element in a view and then clicking _enter_. Once inside a selection, you can switch to a different view, and the new view will be applied in the context of the selection. For example, if you drill down into a process called foo and then switch to the _Connections_ view, the output will include only the connections made or received by _foo_. To drill down multiple times, keep clicking _enter_. For example, you can click on a container in the _Containers_ view to get the processes running inside it, and then click on one of the processes to see its threads. ACTIONS AND HOTKEYS ------------------- Each view has a list of command lines that can be executed in the context of the current selection by pressing 'hotkeys'. For example, pressing 'k' in the Processes view kills the selected process, pressing 'b' in the Containers view opens a bash shell in the selected container. Each view supports different actions. You can see which actions a view supports by pressing F8. You can customize the view's actions by editing the view's Lua file. CONTAINERS SUPPORT ------------------ Starting csysdig with the -pc command line switch will cause many of the views to include additional container information. For example, the _Processes_ will include a column showing the container the process belongs to. Similarly, the _Connections_ view will show which container each connection belongs to. INTERACTIVE COMMANDS -------------------- ## Views Window ## **Arrows, PgUP, PgDn, Home, End** Change the selection and scroll view content, both vertically and horizontally. **Enter** Drill down into the currently highlighted entry. **Backspace** Navigate back to the previous view. **F2** Show the view picker. This will let you switch to another view. **CTRL+F /** Incremental search in the list of view entries. **F4** Incremental filtering of the view entries. **F5, e** 'echo FDs' for the selection, i.e. view FD input/output for the currently highlighted entry. **F6, d** 'dig' into the selection, i.e. view sysdig events for the currently highlighted entry. Refer to the sysdig man page to learn about interpreting the content of this window. **F7** Show the help page for the currently displayed view. **F8** Open the view's actions panel. **F9, >** Open the column sort panel. **F10, q** Quit. **DEL, c** For views that are listing elements without aggregating them by key (identifiable by yellow column headers), this command clears the view content. **p** Pause screen updates. **` <1-9>`** sort column `` **F1, h, ?** Show the help screen. ## Echo and sysdig Windows ## **Arrows, PgUP, PgDn, Home, End** Scroll the page content. **Backspace** Navigate back to the previous view. **CTRL+F /** Search inside the window content. **F3** Find Next. **F2** Chose the output rendering format. Options are 'Dotted ASCII' (non-printable binary bytes are rendered as dots), 'Printable ASCII' (non-printable binary bytes are not included and line endings are rendered accurately) and 'Hex' (dotted ASCII representation is included together with the Hexadecimal rendering of the buffers). **DEL, c** Clear the screen content. **p** Pause screen updates. **CTRL+G** Go to line. ## Spectrogram Window ## **F2** Show the view picker. This will let you switch to another view. **p** Pause/Resume the visualization. **Backspace** Navigate back to the previous view. MOUSE USAGE ----------- - Clicking on column headers lets you sort the table. - Double clicking on row entries performs a drill down. - Clicking on the filter string at the top of the screen (the text after 'Filter:') lets you change the sysdig filter and customize the view content. - You can use the mouse on the entries in the menu at the bottom of the screen to perform their respective actions. COMMAND LINE OPTIONS -------------------- **-d** _period_, **--delay**=_period_ Set the delay between updates, in milliseconds (by default = 2000). This works similarly to the -d option in top. **-E**, **--exclude-users** Don't create the user/group tables by querying the OS when sysdig starts. This also means that no user or group info will be written to the tracefile by the -w flag. The user/group tables are necessary to use filter fields like user.name or group.name. However, creating them can increase sysdig's startup time. **--force-term-compat** Try to configure simple terminal settings (xterm-1002) that work better with terminals like putty. Try to use this flag if you experience terminal issues like the mouse not working. **-h**, **--help** Print this page **-k**, **--k8s-api** Enable Kubernetes support by connecting to the API server specified as argument. E.g. "http://admin:password@127.0.0.1:8080". The API server can also be specified via the environment variable SYSDIG_K8S_API. **-K** _btfile | certfile:keyfile[#password][:cacertfile]_, **--k8s-api-cert=**_btfile | certfile:keyfile[#password][:cacertfile]_ Use the provided files names to authenticate user and (optionally) verify the K8S API server identity. Each entry must specify full (absolute, or relative to the current directory) path to the respective file. Private key password is optional (needed only if key is password protected). CA certificate is optional. For all files, only PEM file format is supported. Specifying CA certificate only is obsoleted - when single entry is provided for this option, it will be interpreted as the name of a file containing bearer token. Note that the format of this command-line option prohibits use of files whose names contain ':' or '#' characters in the file name. Option can also be provided via the environment variable SYSDIG_K8S_API_CERT. **-l**, **--list** List all the fields that can be used in views. **--logfile** _file_ Print program logs into the given file. **-m** _url[,marathon-url]_, **--mesos-api=**_url[,marathon-url]_ Enable Mesos support by connecting to the API server specified as argument (e.g. http://admin:password@127.0.0.1:5050). Mesos url is required. Marathon url is optional, defaulting to auto-follow - if Marathon API server is not provided, csysdig will attempt to retrieve (and subsequently follow, if it migrates) the location of Marathon API server from the Mesos master. Note that, with auto-follow, csysdig will likely receive a cluster internal IP address for Marathon API server, so running csysdig with Marathon auto-follow from a node that is not part of Mesos cluster may not work. Additionally, running csysdig with Mesos support on a node that has no containers managed by Mesos is of limited use because, although cluster metadata will be collected, there will be no Mesos/Marathon filtering capability. The API servers can also be specified via the environment variable SYSDIG_MESOS_API. **-n** _num_, **--numevents**=_num_ Stop capturing after _num_ events **--page-faults** Capture user/kernel major/minor page faults **-pc**, **-pcontainers**_ Instruct csysdig to use a container-friendly format in its views. This will cause several of the views to contain additional container-related columns. **-R**, **--resolve-ports** Resolve port numbers to names. **-r** _readfile_, **--read**=_readfile_ Read the events from _readfile_. **-s** _len_, **--snaplen**=_len_ Capture the first _len_ bytes of each I/O buffer. By default, the first 80 bytes are captured. Use this option with caution, it can generate huge trace files. **-T**, **--force-tracers-capture** Tell the driver to make sure full buffers are captured from /dev/null, to make sure that tracers are completely captured. Note that sysdig will enable extended /dev/null capture by itself after detecting that tracers are written there, but that could result in the truncation of some tracers at the beginning of the capture. This option allows preventing that. **-v** _view_id_, **--views**=_view_id_ Run the view with the given ID when csysdig starts. View IDs can be found in the view documentation pages in csysdig. Combine this option with a command line filter for complete output customization. **--version** Print version number. FILTERING --------- Similarly to what you do with sysdig, you can specify a filter on the command line to restrict the events that csysdig processes. To modify the filter while the program is running, or to add a filter at runtime, click on the filter text in the UI with the mouse. CUSTOMIZING CSYSDIG ------------------- csysdig is completely customizable. This means that you can modify any of the csysdig views, and even create your own views. Like sysdig chisels, csysdig views are Lua scripts. Full information can be found at the following github wiki page: https://github.com/draios/sysdig/wiki/csysdig-View-Format-Reference. FILES ----- */usr/share/sysdig/chisels* The global views directory. *~/.chisels* The personal views directory. AUTHOR ------ Draios Inc. (dba Sysdig) SEE ALSO -------- **sysdig**(8), **strace**(8), **tcpdump**(8), **lsof**(8) sysdig-0.26.4/userspace/sysdig/man/sysdig.8000066400000000000000000000402121352731327100205520ustar00rootroot00000000000000.\" Automatically generated by Pandoc 1.19.2.1 .\" .TH "" "" "" "" "" .hy .SS NAME .PP sysdig \- the definitive system and process troubleshooting tool .SS SYNOPSIS .PP \f[B]sysdig\f[] [\f[I]option\f[]]... [\f[I]filter\f[]] .SS DESCRIPTION .PP \f[B]Note: if you are interested in an easier to use interface for the sysdig functionality, use the csysdig command line utility.\f[] .PP sysdig is a tool for system troubleshooting, analysis and exploration. It can be used to capture, filter and decode system calls and other OS events. .PD 0 .P .PD sysdig can be both used to inspect live systems, or to generate trace files that can be analyzed at a later stage. .PP sysdig includes a powerul filtering language, has customizable output, and can be extended through Lua scripts, called chisels. .PP \f[B]Output format\f[] .PP By default, sysdig prints the information for each captured event on a single line, with the following format: .PP \f[C]*%evt.num\ %evt.time\ %evt.cpu\ %proc.name\ (%thread.tid)\ %evt.dir\ %evt.type\ %evt.info\f[] .PP where: .IP \[bu] 2 evt.num is the incremental event number .IP \[bu] 2 evt.time is the event timestamp .IP \[bu] 2 evt.cpu is the CPU number where the event was captured .IP \[bu] 2 proc.name is the name of the process that generated the event .IP \[bu] 2 thread.tid id the TID that generated the event, which corresponds to the PID for single thread processes .IP \[bu] 2 evt.dir is the event direction, > for enter events and < for exit events .IP \[bu] 2 evt.type is the name of the event, e.g. \[aq]open\[aq] or \[aq]read\[aq] .IP \[bu] 2 evt.args is the list of event arguments. .PP The output format can be customized with the \-p switch, using any of the fields listed by \[aq]sysdig \-l\[aq]. .PP Using \-pc or \-pcontainer, the default format will be changed to a container\-friendly one: .PP \f[C]*%evt.num\ %evt.time\ %evt.cpu\ %container.name\ (%container.id)\ %proc.name\ (%thread.tid:%thread.vtid)\ %evt.dir\ %evt.type\ %evt.info\f[] .PP \f[B]Trace Files\f[] .PP A trace file can be created using the \-w switch: .RS .PP $ sysdig \-w trace.scap .RE .PP The \-s switch can be used to specify how many bytes of each data buffer should be saved to disk. And filters can be .PD 0 .P .PD used to save only certain events to disk: .RS .PP $ sysdig \-s 2000 \-w trace.scap proc.name=cat .RE .PP Trace files can be read this using the \-r switch: .RS .PP $ sysdig \-r trace.scap .RE .PP \f[B]Filtering\f[] .PP sysdig filters are specified at the end of the command line. The simplest filter is a basic field\-value check: .RS .PP $ sysdig proc.name=cat .RE .PP The list of available fields can be obtained with \[aq]sysdig \-l\[aq]. .PD 0 .P .PD Filter expressions can use one of these comparison operators: \f[I]=\f[], \f[I]!=\f[], \f[I]<\f[], \f[I]<=\f[], \f[I]>\f[], \f[I]>=\f[], \f[I]contains\f[], \f[I]icontains\f[], \f[I]in\f[] and \f[I]exists\f[]. e.g. .RS .PP $ sysdig fd.name contains /etc .PD 0 .P .PD $ sysdig "evt.type in ( \[aq]select\[aq], \[aq]poll\[aq] )" .PD 0 .P .PD $ sysdig proc.name exists .RE .PP Multiple checks can be combined through brackets and the following boolean operators: \f[I]and\f[], \f[I]or\f[], \f[I]not\f[]. e.g. .RS .PP $ sysdig "not (fd.name contains /proc or fd.name contains /dev)" .RE .PP \f[B]Chisels\f[] .PP sysdig\[aq]s chisels are little scripts that analyze the sysdig event stream to perform useful actions. .PD 0 .P .PD To get the list of available chisels, type .RS .PP $ sysdig \-cl .RE .PP To get details about a specific chisel, type .RS .PP $ sysdig \-i spy_ip .RE .PP To run one of the chisels, you use the \-c flag, e.g. .RS .PP $ sysdig \-c topfiles_bytes .RE .PP If a chisel needs arguments, you specify them after the chisel name: .RS .PP $ sysdig \-c spy_ip 192.168.1.157 .RE .PP If a chisel has more than one argument, specify them after the chisel name, enclosed in quotes: .RS .PP $ sysdig \-c chisel_name "arg1 arg2 arg3" .RE .PP Chisels can be combined with filters: .RS .PP $ sysdig \-c topfiles_bytes "not fd.name contains /dev" .RE .SS OPTIONS .PP \f[B]\-A\f[], \f[B]\-\-print\-ascii\f[] .PD 0 .P .PD Only print the text portion of data buffers, and echo end\-of\-lines. This is useful to only display human\-readable data. .PP \f[B]\-b\f[], \f[B]\-\-print\-base64\f[] .PD 0 .P .PD Print data buffers in base64. This is useful for encoding binary data that needs to be used over media designed to handle textual data (i.e., terminal or json). .PP \f[B]\-c\f[] \f[I]chiselname\f[] \f[I]chiselargs\f[], \f[B]\-\-chisel\f[]=\f[I]chiselname\f[] \f[I]chiselargs\f[] .PD 0 .P .PD run the specified chisel. If the chisel require arguments, they must be specified in the command line after the name. .PP \f[B]\-C\f[] \f[I]filesize\f[] .PD 0 .P .PD Break a capture into separate files, and limit the size of each file based on the specified number of megabytes. The units of \f[I]filesize\f[] are millions of bytes (10^6, not 2^20). Use in conjunction with \f[B]\-W\f[] to enable automatic file rotation. Otherwise, new files will continue to be created until the capture is manually stopped. .PP Files will have the name specified by \f[B]\-w\f[] with a counter added starting at 0. .PP \f[B]\-cl\f[], \f[B]\-\-list\-chisels\f[] .PD 0 .P .PD lists the available chisels. Looks for chisels in ./chisels, ~/.chisels and /usr/share/sysdig/chisels. .PP \f[B]\-d\f[], \f[B]\-\-displayflt\f[] .PD 0 .P .PD Make the given filter a display one. Setting this option causes the events to be filtered after being parsed by the state system. Events are normally filtered before being analyzed, which is more efficient, but can cause state (e.g. FD names) to be lost. .PP \f[B]\-D\f[], \f[B]\-\-debug\f[] .PD 0 .P .PD Capture events about sysdig itself and print additional logging on standard error. .PP \f[B]\-E\f[], \f[B]\-\-exclude\-users\f[] .PD 0 .P .PD Don\[aq]t create the user/group tables by querying the OS when sysdig starts. This also means that no user or group info will be written to the tracefile by the \f[B]\-w\f[] flag. The user/group tables are necessary to use filter fields like user.name or group.name. However, creating them can increase sysdig\[aq]s startup time. Moreover, they contain information that could be privacy sensitive. .PP \f[B]\-e\f[] \f[I]numevents\f[] .PD 0 .P .PD Break a capture into separate files, and limit the size of each file based on the specified number of events. Use in conjunction with \f[B]\-W\f[] to enable automatic file rotation. Otherwise, new files will continue to be created until the capture is manually stopped. .PP Files will have the name specified by \f[B]\-w\f[] with a counter added starting at 0. .PP \f[B]\-F\f[], \f[B]\-\-fatfile\f[] .PD 0 .P .PD Enable fatfile mode. When writing in fatfile mode, the output file will contain events that will be invisible when reading the file, but that are necessary to fully reconstruct the state. Fatfile mode is useful when saving events to disk with an aggressive filter. The filter could drop events that would the state to be updated (e.g. clone() or open()). With fatfile mode, those events are still saved to file, but \[aq]hidden\[aq] so that they won\[aq]t appear when reading the file. Be aware that using this flag might generate substantially bigger traces files. .PP \f[B]\-\-filter\-proclist\f[] .PD 0 .P .PD apply the filter to the process table. A full dump of /proc is typically included in any trace file to make sure all the state required to decode events is in the file. This could cause the file to contain unwanted or sensitive information. Using this flag causes the command line filter to be applied to the /proc dump as well. .PP \f[B]\-G\f[] \f[I]numseconds\f[] .PD 0 .P .PD Break a capture into separate files, and limit the size of each file based on the specified number of seconds. Use in conjunction with \f[B]\-W\f[] to enable automatic file rotation. Otherwise, new files will continue to be created until the capture is manually stopped. .PP Files will have the name specified by \f[B]\-w\f[] which should include a time format as defined by strftime(3). If no time format is specified, a counter will be used. .PP \f[B]\-h\f[], \f[B]\-\-help\f[] .PD 0 .P .PD Print this page .PP \f[B]\-i \f[I]chiselname\f[]\f[], \f[B]\-\-chisel\-info=\f[]\f[I]chiselname\f[] .PD 0 .P .PD Get a longer description and the arguments associated with a chisel found in the \-cl option list. .PP \f[B]\-j\f[], \f[B]\-\-json\f[] .PD 0 .P .PD Emit output as json, data buffer encoding will depend from the print format selected. .PP \f[B]\-k\f[], \f[B]\-\-k8s\-api\f[] .PD 0 .P .PD Enable Kubernetes support by connecting to the API server specified as argument. E.g. "". The API server can also be specified via the environment variable SYSDIG_K8S_API. .PP \f[B]\-K\f[] \f[I]btfile | certfile:keyfile[#password][:cacertfile]\f[], \f[B]\-\-k8s\-api\-cert=\f[]\f[I]btfile | certfile:keyfile[#password][:cacertfile]\f[] .PD 0 .P .PD Use the provided files names to authenticate user and (optionally) verify the K8S API server identity. Each entry must specify full (absolute, or relative to the current directory) path to the respective file. Private key password is optional (needed only if key is password protected). CA certificate is optional. For all files, only PEM file format is supported. Specifying CA certificate only is obsoleted \- when single entry is provided for this option, it will be interpreted as the name of a file containing bearer token. Note that the format of this command\-line option prohibits use of files whose names contain \[aq]:\[aq] or \[aq]#\[aq] characters in the file name. Option can also be provided via the environment variable SYSDIG_K8S_API_CERT. .PP \f[B]\-L\f[], \f[B]\-\-list\-events\f[] .PD 0 .P .PD List the events that the engine supports .PP \f[B]\-l\f[], \f[B]\-\-list\f[] .PD 0 .P .PD List the fields that can be used for filtering and output formatting. Use \-lv to get additional information for each field. .PP \f[B]\-m\f[] \f[I]url[,marathon\-url]\f[], \f[B]\-\-mesos\-api=\f[]\f[I]url[,marathon\-url]\f[] .PD 0 .P .PD Enable Mesos support by connecting to the API server specified as argument (e.g. ). Mesos url is required. Marathon url is optional, defaulting to auto\-follow \- if Marathon API server is not provided, sysdig will attempt to retrieve (and subsequently follow, if it migrates) the location of Marathon API server from the Mesos master. Note that, with auto\-follow, sysdig will likely receive a cluster internal IP address for Marathon API server, so running sysdig with Marathon auto\-follow from a node that is not part of Mesos cluster may not work. Additionally, running sysdig with Mesos support on a node that has no containers managed by Mesos is of limited use because, although cluster metadata will be collected, there will be no Mesos/Marathon filtering capability. The API servers can also be specified via the environment variable SYSDIG_MESOS_API. .PP \f[B]\-M\f[] \f[I]num_seconds\f[] .PD 0 .P .PD Stop collecting after reaching .PP \f[B]\-n\f[] \f[I]num\f[], \f[B]\-\-numevents\f[]=\f[I]num\f[] .PD 0 .P .PD Stop capturing after \f[I]num\f[] events .PP \f[B]\-\-page\-faults\f[] .PD 0 .P .PD Capture user/kernel major/minor page faults .PP \f[B]\-P\f[], \f[B]\-\-progress\f[] .PD 0 .P .PD Print progress on stderr while processing trace files. .PP \f[B]\-p\f[] \f[I]outputformat\f[], \f[B]\-\-print\f[]=\f[I]outputformat\f[] .PD 0 .P .PD Specify the format to be used when printing the events. With \-pc or \-pcontainer will use a container\-friendly format. With \-pk or \-pkubernetes will use a kubernetes\-friendly format. With \-pm or \-pmesos will use a mesos\-friendly format. Specifying \f[B]\-pp\f[] on the command line will cause sysdig to print the default command line format and exit. .PP \f[B]\-q\f[], \f[B]\-\-quiet\f[] .PD 0 .P .PD Don\[aq]t print events on the screen. Useful when dumping to disk. .PP \f[B]\-r\f[] \f[I]readfile\f[], \f[B]\-\-read\f[]=\f[I]readfile\f[] .PD 0 .P .PD Read the events from \f[I]readfile\f[]. .PP \f[B]\-R\f[], \f[B]\-\-resolve\-ports\f[] .PD 0 .P .PD Resolve port numbers to names. .PP \f[B]\-S\f[], \f[B]\-\-summary\f[] .PD 0 .P .PD print the event summary (i.e. the list of the top events) when the capture ends. .PP \f[B]\-s\f[] \f[I]len\f[], \f[B]\-\-snaplen\f[]=\f[I]len\f[] .PD 0 .P .PD Capture the first \f[I]len\f[] bytes of each I/O buffer. By default, the first 80 bytes are captured. Use this option with caution, it can generate huge trace files. .PP \f[B]\-t\f[] \f[I]timetype\f[], \f[B]\-\-timetype\f[]=\f[I]timetype\f[] .PD 0 .P .PD Change the way event time is displayed. Accepted values are \f[B]h\f[] for human\-readable string, \f[B]a\f[] for absolute timestamp from epoch, \f[B]r\f[] for relative time from the first displayed event, \f[B]d\f[] for delta between event enter and exit, and \f[B]D\f[] for delta from the previous event. .PP \f[B]\-T\f[], \f[B]\-\-force\-tracers\-capture\f[] .PD 0 .P .PD Tell the driver to make sure full buffers are captured from /dev/null, to make sure that tracers are completely captured. Note that sysdig will enable extended /dev/null capture by itself after detecting that tracers are written there, but that could result in the truncation of some tracers at the beginning of the capture. This option allows preventing that. .PP \f[B]\-\-unbuffered\f[] .PD 0 .P .PD Turn off output buffering. This causes every single line emitted by sysdig to be flushed, which generates higher CPU usage but is useful when piping sysdig\[aq]s output into another process or into a script. .PP \f[B]\-v\f[], \f[B]\-\-verbose\f[] .PD 0 .P .PD Verbose output. This flag will cause the full content of text and binary buffers to be printed on screen, instead of being truncated to 40 characters. Note that data buffers length is still limited by the snaplen (refer to the \-s flag documentation) \-v will also make sysdig print some summary information at the end of the capture. .PP \f[B]\-\-version\f[] .PD 0 .P .PD Print version number. .PP \f[B]\-w\f[] \f[I]writefile\f[], \f[B]\-\-write\f[]=\f[I]writefile\f[] .PD 0 .P .PD Write the captured events to \f[I]writefile\f[]. .PP \f[B]\-W\f[] \f[I]num\f[] .PD 0 .P .PD Turn on file rotation for continuous capture, and limit the number of files created to the specified number. Once the cap is reached, older files will be overwritten (ring buffer). Use in conjunction with the \f[B]\-C\f[] / \f[B]\-G\f[] / \f[B]\-e\f[] options to limit the size of each file based on number of megabytes, seconds, and/or events (respectively). .PP \f[B]\-x\f[], \f[B]\-\-print\-hex\f[] .PD 0 .P .PD Print data buffers in hex. .PP \f[B]\-X\f[], \f[B]\-\-print\-hex\-ascii\f[] .PD 0 .P .PD Print data buffers in hex and ASCII. .PP \f[B]\-z\f[], \f[B]\-\-compress\f[] .PD 0 .P .PD Used with \f[B]\-w\f[], enables compression for tracefiles. .SS EXAMPLES .PP Capture all the events from the live system and print them to screen .RS .PP $ sysdig .RE .PP Capture all the events from the live system and save them to disk .RS .PP $ sysdig \-w dumpfile.scap .RE .PP Capture all the events in the latest 24 hours and save them to disk organized in files containing 1 hour of system activity each .RS .PP $ sysdig \-G 3600 \-W 24 \-w dumpfile.scap .RE .PP Read events from a file and print them to screen .RS .PP $ sysdig \-r dumpfile.scap .RE .PP Prepare a sanitized version of a system capture .RS .PP $ sysdig \-r dumpfile.scap \[aq]not evt.buffer contains foo\[aq] \-w cleandump.scap .RE .PP Print all the open system calls invoked by cat .RS .PP $ sysdig proc.name=cat and evt.type=open .RE .PP Print the name of the files opened by cat .RS .PP $ sysdig \-p"%evt.arg.name" proc.name=cat and evt.type=open .RE .PP List the available chisels .RS .PP $ sysdig \-cl .RE .PP Use the spy_ip chisel to look at the data exchanged with 192.168.1.157: .RS .PP $ sysdig \-c spy_ip 192.168.1.157 .RE .SS FILES .PP \f[I]/usr/share/sysdig/chisels\f[] .PD 0 .P .PD The global chisels directory. .PP \f[I]~/.chisels\f[] .PD 0 .P .PD The personal chisels directory. .SS BUGS .IP \[bu] 2 sysdig and its chisels are designed to be used with LuaJIT in Lua 5.1 mode. While it is possible to use sysdig with LuaJIT in Lua 5.2 mode or regular Lua, some chisels may not work as expected. .SS AUTHOR .PP Draios Inc. aka sysdig .SS SEE ALSO .PP \f[B]csysdig\f[](8), \f[B]strace\f[](8), \f[B]tcpdump\f[](8), \f[B]lsof\f[](8) sysdig-0.26.4/userspace/sysdig/man/sysdig.md000066400000000000000000000342221352731327100210070ustar00rootroot00000000000000NAME ---- sysdig - the definitive system and process troubleshooting tool SYNOPSIS -------- **sysdig** [*option*]... [*filter*] DESCRIPTION ----------- **Note: if you are interested in an easier to use interface for the sysdig functionality, use the csysdig command line utility.** sysdig is a tool for system troubleshooting, analysis and exploration. It can be used to capture, filter and decode system calls and other OS events. sysdig can be both used to inspect live systems, or to generate trace files that can be analyzed at a later stage. sysdig includes a powerul filtering language, has customizable output, and can be extended through Lua scripts, called chisels. **Output format** By default, sysdig prints the information for each captured event on a single line, with the following format: ```*%evt.num %evt.time %evt.cpu %proc.name (%thread.tid) %evt.dir %evt.type %evt.info``` where: * evt.num is the incremental event number * evt.time is the event timestamp * evt.cpu is the CPU number where the event was captured * proc.name is the name of the process that generated the event * thread.tid id the TID that generated the event, which corresponds to the PID for single thread processes * evt.dir is the event direction, > for enter events and < for exit events * evt.type is the name of the event, e.g. 'open' or 'read' * evt.args is the list of event arguments. The output format can be customized with the -p switch, using any of the fields listed by 'sysdig -l'. Using -pc or -pcontainer, the default format will be changed to a container-friendly one: ```*%evt.num %evt.time %evt.cpu %container.name (%container.id) %proc.name (%thread.tid:%thread.vtid) %evt.dir %evt.type %evt.info``` **Trace Files** A trace file can be created using the -w switch: > $ sysdig -w trace.scap The -s switch can be used to specify how many bytes of each data buffer should be saved to disk. And filters can be used to save only certain events to disk: > $ sysdig -s 2000 -w trace.scap proc.name=cat Trace files can be read this using the -r switch: > $ sysdig -r trace.scap **Filtering** sysdig filters are specified at the end of the command line. The simplest filter is a basic field-value check: > $ sysdig proc.name=cat The list of available fields can be obtained with 'sysdig -l'. Filter expressions can use one of these comparison operators: _=_, _!=_, _<_, _<=_, _>_, _>=_, _contains_, _icontains_, _in_ and _exists_. e.g. > $ sysdig fd.name contains /etc > $ sysdig "evt.type in ( 'select', 'poll' )" > $ sysdig proc.name exists Multiple checks can be combined through brackets and the following boolean operators: _and_, _or_, _not_. e.g. > $ sysdig "not (fd.name contains /proc or fd.name contains /dev)" **Chisels** sysdig's chisels are little scripts that analyze the sysdig event stream to perform useful actions. To get the list of available chisels, type > $ sysdig -cl To get details about a specific chisel, type > $ sysdig -i spy_ip To run one of the chisels, you use the -c flag, e.g. > $ sysdig -c topfiles_bytes If a chisel needs arguments, you specify them after the chisel name: > $ sysdig -c spy_ip 192.168.1.157 If a chisel has more than one argument, specify them after the chisel name, enclosed in quotes: > $ sysdig -c chisel_name "arg1 arg2 arg3" Chisels can be combined with filters: > $ sysdig -c topfiles_bytes "not fd.name contains /dev" OPTIONS ------- **-A**, **--print-ascii** Only print the text portion of data buffers, and echo end-of-lines. This is useful to only display human-readable data. **-b**, **--print-base64** Print data buffers in base64. This is useful for encoding binary data that needs to be used over media designed to handle textual data (i.e., terminal or json). **-c** _chiselname_ _chiselargs_, **--chisel**=_chiselname_ _chiselargs_ run the specified chisel. If the chisel require arguments, they must be specified in the command line after the name. **-C** _filesize_ Break a capture into separate files, and limit the size of each file based on the specified number of megabytes. The units of _filesize_ are millions of bytes (10^6, not 2^20). Use in conjunction with **-W** to enable automatic file rotation. Otherwise, new files will continue to be created until the capture is manually stopped. Files will have the name specified by **-w** with a counter added starting at 0. **-cl**, **--list-chisels** lists the available chisels. Looks for chisels in ./chisels, ~/.chisels and /usr/share/sysdig/chisels. **-d**, **--displayflt** Make the given filter a display one. Setting this option causes the events to be filtered after being parsed by the state system. Events are normally filtered before being analyzed, which is more efficient, but can cause state (e.g. FD names) to be lost. **-D**, **--debug** Capture events about sysdig itself, display internal events in addition to system events, and print additional logging on standard error. **-E**, **--exclude-users** Don't create the user/group tables by querying the OS when sysdig starts. This also means that no user or group info will be written to the tracefile by the **-w** flag. The user/group tables are necessary to use filter fields like user.name or group.name. However, creating them can increase sysdig's startup time. Moreover, they contain information that could be privacy sensitive. **-e** _numevents_ Break a capture into separate files, and limit the size of each file based on the specified number of events. Use in conjunction with **-W** to enable automatic file rotation. Otherwise, new files will continue to be created until the capture is manually stopped. Files will have the name specified by **-w** with a counter added starting at 0. **-F**, **--fatfile** Enable fatfile mode. When writing in fatfile mode, the output file will contain events that will be invisible when reading the file, but that are necessary to fully reconstruct the state. Fatfile mode is useful when saving events to disk with an aggressive filter. The filter could drop events that would cause the state to be updated (e.g. clone() or open()). With fatfile mode, those events are still saved to file, but 'hidden' so that they won't appear when reading the file. Be aware that using this flag might generate substantially bigger traces files. **--filter-proclist** apply the filter to the process table. A full dump of /proc is typically included in any trace file to make sure all the state required to decode events is in the file. This could cause the file to contain unwanted or sensitive information. Using this flag causes the command line filter to be applied to the /proc dump as well. **-G** _numseconds_ Break a capture into separate files, and limit the size of each file based on the specified number of seconds. Use in conjunction with **-W** to enable automatic file rotation. Otherwise, new files will continue to be created until the capture is manually stopped. Files will have the name specified by **-w** which should include a time format as defined by strftime(3). If no time format is specified, a counter will be used. **-h**, **--help** Print this page **-i _chiselname_**, **--chisel-info=**_chiselname_ Get a longer description and the arguments associated with a chisel found in the -cl option list. **-j**, **--json** Emit output as json, data buffer encoding will depend from the print format selected. **-k**, **--k8s-api** Enable Kubernetes support by connecting to the API server specified as argument. E.g. "http://admin:password@127.0.0.1:8080". The API server can also be specified via the environment variable SYSDIG_K8S_API. **-K** _btfile | certfile:keyfile[#password][:cacertfile]_, **--k8s-api-cert=**_btfile | certfile:keyfile[#password][:cacertfile]_ Use the provided files names to authenticate user and (optionally) verify the K8S API server identity. Each entry must specify full (absolute, or relative to the current directory) path to the respective file. Private key password is optional (needed only if key is password protected). CA certificate is optional. For all files, only PEM file format is supported. Specifying CA certificate only is obsoleted - when single entry is provided for this option, it will be interpreted as the name of a file containing bearer token. Note that the format of this command-line option prohibits use of files whose names contain ':' or '#' characters in the file name. Option can also be provided via the environment variable SYSDIG_K8S_API_CERT. **-L**, **--list-events** List the events that the engine supports **-l**, **--list** List the fields that can be used for filtering and output formatting. Use -lv to get additional information for each field. **--list-markdown** Like -l, but produces markdown output **-m** _url[,marathon-url]_, **--mesos-api=**_url[,marathon-url]_ Enable Mesos support by connecting to the API server specified as argument (e.g. http://admin:password@127.0.0.1:5050). Mesos url is required. Marathon url is optional, defaulting to auto-follow - if Marathon API server is not provided, sysdig will attempt to retrieve (and subsequently follow, if it migrates) the location of Marathon API server from the Mesos master. Note that, with auto-follow, sysdig will likely receive a cluster internal IP address for Marathon API server, so running sysdig with Marathon auto-follow from a node that is not part of Mesos cluster may not work. Additionally, running sysdig with Mesos support on a node that has no containers managed by Mesos is of limited use because, although cluster metadata will be collected, there will be no Mesos/Marathon filtering capability. The API servers can also be specified via the environment variable SYSDIG_MESOS_API. **-M** _num_seconds_ Stop collecting after reaching **-n** _num_, **--numevents**=_num_ Stop capturing after _num_ events **--page-faults** Capture user/kernel major/minor page faults **-P**, **--progress** Print progress on stderr while processing trace files. **-p** _outputformat_, **--print**=_outputformat_ Specify the format to be used when printing the events. With -pc or -pcontainer will use a container-friendly format. With -pk or -pkubernetes will use a kubernetes-friendly format. With -pm or -pmesos will use a mesos-friendly format. Specifying **-pp** on the command line will cause sysdig to print the default command line format and exit. **-q**, **--quiet** Don't print events on the screen. Useful when dumping to disk. **-r** _readfile_, **--read**=_readfile_ Read the events from _readfile_. **-R**, **--resolve-ports** Resolve port numbers to names. **-S**, **--summary** print the event summary (i.e. the list of the top events) when the capture ends. **-s** _len_, **--snaplen**=_len_ Capture the first _len_ bytes of each I/O buffer. By default, the first 80 bytes are captured. Use this option with caution, it can generate huge trace files. **-t** _timetype_, **--timetype**=_timetype_ Change the way event time is displayed. Accepted values are **h** for human-readable string, **a** for absolute timestamp from epoch, **r** for relative time from the first displayed event, **d** for delta between event enter and exit, and **D** for delta from the previous event. **-T**, **--force-tracers-capture** Tell the driver to make sure full buffers are captured from /dev/null, to make sure that tracers are completely captured. Note that sysdig will enable extended /dev/null capture by itself after detecting that tracers are written there, but that could result in the truncation of some tracers at the beginning of the capture. This option allows preventing that. **--unbuffered** Turn off output buffering. This causes every single line emitted by sysdig to be flushed, which generates higher CPU usage but is useful when piping sysdig's output into another process or into a script. **-v**, **--verbose** Verbose output. This flag will cause the full content of text and binary buffers to be printed on screen, instead of being truncated to 40 characters. Note that data buffers length is still limited by the snaplen (refer to the -s flag documentation) -v will also make sysdig print some summary information at the end of the capture. **--version** Print version number. **-w** _writefile_, **--write**=_writefile_ Write the captured events to _writefile_. **-W** _num_ Turn on file rotation for continuous capture, and limit the number of files created to the specified number. Once the cap is reached, older files will be overwritten (ring buffer). Use in conjunction with the **-C** / **-G** / **-e** options to limit the size of each file based on number of megabytes, seconds, and/or events (respectively). **-x**, **--print-hex** Print data buffers in hex. **-X**, **--print-hex-ascii** Print data buffers in hex and ASCII. **-z**, **--compress** Used with **-w**, enables compression for tracefiles. EXAMPLES -------- Capture all the events from the live system and print them to screen > $ sysdig Capture all the events from the live system and save them to disk > $ sysdig -w dumpfile.scap Capture all the events in the latest 24 hours and save them to disk organized in files containing 1 hour of system activity each > $ sysdig -G 3600 -W 24 -w dumpfile.scap Read events from a file and print them to screen > $ sysdig -r dumpfile.scap Prepare a sanitized version of a system capture > $ sysdig -r dumpfile.scap 'not evt.buffer contains foo' -w cleandump.scap Print all the open system calls invoked by cat > $ sysdig proc.name=cat and evt.type=open Print the name of the files opened by cat > $ sysdig -p"%evt.arg.name" proc.name=cat and evt.type=open List the available chisels > $ sysdig -cl Use the spy_ip chisel to look at the data exchanged with 192.168.1.157: > $ sysdig -c spy_ip 192.168.1.157 FILES ----- */usr/share/sysdig/chisels* The global chisels directory. *~/.chisels* The personal chisels directory. BUGS ---- * sysdig and its chisels are designed to be used with LuaJIT in Lua 5.1 mode. While it is possible to use sysdig with LuaJIT in Lua 5.2 mode or regular Lua, some chisels may not work as expected. AUTHOR ------ Draios Inc. aka sysdig SEE ALSO -------- **csysdig**(8), **strace**(8), **tcpdump**(8), **lsof**(8) sysdig-0.26.4/userspace/sysdig/sysdig.cpp000066400000000000000000001343001352731327100204140ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include #include "chisel.h" #include "sysdig.h" #include "utils.h" #ifdef _WIN32 #include "win32/getopt.h" #include #else #include #include #endif static bool g_terminate = false; #ifdef HAS_CHISELS vector g_chisels; #endif static void usage(); // // Helper functions // static void signal_callback(int signal) { g_terminate = true; } // // Program help // static void usage() { printf( "sysdig version " SYSDIG_VERSION "\n" "Usage: sysdig [options] [-p ] [filter]\n\n" "Options:\n" " -A, --print-ascii Only print the text portion of data buffers, and echo\n" " end-of-lines. This is useful to only display human-readable\n" " data.\n" " -b, --print-base64 Print data buffers in base64. This is useful for encoding\n" " binary data that needs to be used over media designed to\n" " handle textual data (i.e., terminal or json).\n" " -B, --bpf=\n" " Enable live capture using the specified BPF probe instead of the kernel module.\n" " The BPF probe can also be specified via the environment variable\n" " SYSDIG_BPF_PROBE. If is left empty, sysdig will\n" " try to load one from the sysdig-probe-loader script.\n" #ifdef HAS_CHISELS " -c , --chisel \n" " run the specified chisel. If the chisel require arguments,\n" " they must be specified in the command line after the name.\n" " -cl, --list-chisels\n" " lists the available chisels. Looks for chisels in\n" " ./chisels, ~/.chisels and /usr/share/sysdig/chisels.\n" #endif " -C , --file-size=\n" " Before writing an event, check whether the file is\n" " currently larger than file_size and, if so, close the\n" " current file and open a new one. Saved files will have the\n" " name specified with the -w flag, with a number after it,\n" " starting at 0 and continuing upward. The units of file_size\n" " are millions of bytes (10^6, not 2^20). Use the -W flag to\n" " determine how many files will be saved to disk.\n" #ifdef HAS_CAPTURE " --cri Path to CRI socket for container metadata\n" " Use the specified socket to fetch data from a CRI-compatible runtime\n" "\n" " --cri-timeout \n" " Wait at most milliseconds for response from CRI\n" #endif " -d, --displayflt Make the given filter a display one\n" " Setting this option causes the events to be filtered\n" " after being parsed by the state system. Events are\n" " normally filtered before being analyzed, which is more\n" " efficient, but can cause state (e.g. FD names) to be lost.\n" " -D, --debug Capture events about sysdig itself, display internal events\n" " in addition to system events, and print additional\n" " logging on standard error.\n" " -E, --exclude-users\n" " Don't create the user/group tables by querying the OS when\n" " sysdig starts. This also means that no user or group info\n" " will be written to the trace file by the -w flag.\n" " The user/group tables are necessary to use filter fields\n" " like user.name or group.name. However, creating them can\n" " increase sysdig's startup time. Moreover, they contain\n" " information that could be privacy sensitive.\n" " -e If used together with -w option, creates a series of dump files\n" " containing only a specified number of events given in num_events\n" " parameter each.\n" " Used alongside -W flags creates a ring buffer of file containing\n" " num_events each.\n" " -F, --fatfile Enable fatfile mode\n" " when writing in fatfile mode, the output file will contain\n" " events that will be invisible when reading the file, but\n" " that are necessary to fully reconstruct the state.\n" " Fatfile mode is useful when saving events to disk with an\n" " aggressive filter. The filter could drop events that would\n" " the state to be updated (e.g. clone() or open()). With\n" " fatfile mode, those events are still saved to file, but\n" " 'hidden' so that they won't appear when reading the file.\n" " Be aware that using this flag might generate substantially\n" " bigger traces files.\n" " --filter-proclist apply the filter to the process table\n" " a full dump of /proc is typically included in any trace file\n" " to make sure all the state required to decode events is in the\n" " file. This could cause the file to contain unwanted or sensitive\n" " information. Using this flag causes the command line filter to\n" " be applied to the /proc dump as well.\n" " -G , --seconds=\n" " Rotates the dump file specified with the -w option every\n" " num_seconds seconds. Saved files will have the name specified\n" " by -w which should include a time format as defined by strftime(3).\n" " If no time format is specified, a counter will be used.\n" " If no data format is specified, this can be used with -W flag to\n" " create a ring buffer of events.\n" " -h, --help Print this page\n" #ifdef HAS_CHISELS " -i , --chisel-info \n" " Get a longer description and the arguments associated with\n" " a chisel found in the -cl option list.\n" #endif " -j, --json Emit output as json, data buffer encoding will depend from the\n" " print format selected.\n" " -k , --k8s-api=\n" " Enable Kubernetes support by connecting to the API server\n" " specified as argument. E.g. \"http://admin:password@127.0.0.1:8080\".\n" " The API server can also be specified via the environment variable\n" " SYSDIG_K8S_API.\n" " -K | :[:], --k8s-api-cert= | :[:]\n" " Use the provided files names to authenticate user and (optionally) verify the K8S API\n" " server identity.\n" " Each entry must specify full (absolute, or relative to the current directory) path\n" " to the respective file.\n" " Private key password is optional (needed only if key is password protected).\n" " CA certificate is optional. For all files, only PEM file format is supported. \n" " Specifying CA certificate only is obsoleted - when single entry is provided \n" " for this option, it will be interpreted as the name of a file containing bearer token.\n" " Note that the format of this command-line option prohibits use of files whose names contain\n" " ':' or '#' characters in the file name.\n" " Option can also be provided via the environment variable SYSDIG_K8S_API_CERT.\n" " -L, --list-events List the events that the engine supports\n" " -l, --list List the fields that can be used for filtering and output\n" " formatting. Use -lv to get additional information for each\n" " field.\n" " --large-environment\n" " Support environments larger than 4KiB\n" " When the environment is larger than 4KiB, load the whole\n" " environment from /proc instead of truncating to the first 4KiB\n" " This may fail for short-lived processes and in that case\n" " the truncated environment is used instead.\n" " --list-markdown like -l, but produces markdown output\n" " -m , --mesos-api=\n" " Enable Mesos support by connecting to the API server\n" " specified as argument. E.g. \"http://admin:password@127.0.0.1:5050\".\n" " Marathon url is optional and defaults to Mesos address, port 8080.\n" " The API servers can also be specified via the environment variable\n" " SYSDIG_MESOS_API.\n" " -M Stop collecting after reached.\n" " -n , --numevents=\n" " Stop capturing after events\n" " --page-faults Capture user/kernel major/minor page faults\n" " -P, --progress Print progress on stderr while processing trace files\n" " -p , --print=\n" " Specify the format to be used when printing the events.\n" " With -pc or -pcontainer will use a container-friendly format.\n" " With -pk or -pkubernetes will use a kubernetes-friendly format.\n" " With -pm or -pmesos will use a mesos-friendly format.\n" " See the examples section below for more info.\n" " -q, --quiet Don't print events on the screen\n" " Useful when dumping to disk.\n" " -R Resolve port numbers to names.\n" " -r , --read=\n" " Read the events from .\n" " -S, --summary print the event summary (i.e. the list of the top events)\n" " when the capture ends.\n" " -s , --snaplen=\n" " Capture the first bytes of each I/O buffer.\n" " By default, the first 80 bytes are captured. Use this\n" " option with caution, it can generate huge trace files.\n" " -t , --timetype=\n" " Change the way event time is displayed. Accepted values are\n" " h for human-readable string, a for absolute timestamp from\n" " epoch, r for relative time from the beginning of the\n" " capture, d for delta between event enter and exit, and\n" " D for delta from the previous event.\n" " -T, --force-tracers-capture\n" " Tell the driver to make sure full buffers are captured from\n" " /dev/null, to make sure that tracers are completely\n" " captured. Note that sysdig will enable extended /dev/null\n" " capture by itself after detecting that tracers are written\n" " there, but that could result in the truncation of some\n" " tracers at the beginning of the capture. This option allows\n" " preventing that.\n" " --unbuffered Turn off output buffering. This causes every single line\n" " emitted by sysdig to be flushed, which generates higher CPU\n" " usage but is useful when piping sysdig's output into another\n" " process or into a script.\n" " -U, --suppress-comm\n" " Ignore all events from processes having the provided comm.\n" " -v, --verbose Verbose output.\n" " This flag will cause the full content of text and binary\n" " buffers to be printed on screen, instead of being truncated\n" " to 40 characters. Note that data buffers length is still\n" " limited by the snaplen (refer to the -s flag documentation)\n" " -v will also make sysdig print some summary information at\n" " the end of the capture.\n" " --version Print version number.\n" " -w , --write=\n" " Write the captured events to .\n" " -W , --limit \n" " Used in conjunction with the -C option, this will limit the number\n" " of files created to the specified number, and begin overwriting files\n" " from the beginning, thus creating a 'rotating' buffer.\n" "\n" " Used in conjunction with the -G option, this will limit the number\n" " of rotated dump files that get created, exiting with status 0 when\n" " reaching the limit. If used with -C as well, the behavior will result\n" " in cyclical files per timeslice.\n" " -x, --print-hex Print data buffers in hex.\n" " -X, --print-hex-ascii\n" " Print data buffers in hex and ASCII.\n" " -z, --compress Used with -w, enables compression for trace files.\n" "\n" "Output format:\n\n" "By default, sysdig prints the information for each captured event on a single\n" " line with the following format:\n\n" " %%evt.num %%evt.outputtime %%evt.cpu %%proc.name (%%thread.tid) %%evt.dir %%evt.type %%evt.info\n\n" "where:\n" " evt.num is the incremental event number\n" " evt.time is the event timestamp\n" " evt.cpu is the CPU number where the event was captured\n" " proc.name is the name of the process that generated the event\n" " thread.tid id the TID that generated the event, which corresponds to the\n" " PID for single thread processes\n" " evt.dir is the event direction, > for enter events and < for exit events\n" " evt.type is the name of the event, e.g. 'open' or 'read'\n" " evt.info is the list of event arguments.\n\n" "The output format can be customized with the -p switch, using any of the\n" "fields listed by 'sysdig -l'.\n\n" "Using -pc or -pcontainer, the default format will be changed to a container-friendly one:\n\n" "%%evt.num %%evt.outputtime %%evt.cpu %%container.name (%%container.id) %%proc.name (%%thread.tid:%%thread.vtid) %%evt.dir %%evt.type %%evt.info\n\n" "Using -pk or -pkubernetes, the default format will be changed to a kubernetes-friendly one:\n\n" "%%evt.num %%evt.outputtime %%evt.cpu %%k8s.pod.name (%%container.id) %%proc.name (%%thread.tid:%%thread.vtid) %%evt.dir %%evt.type %%evt.info\n\n" "Using -pm or -pmesos, the default format will be changed to a mesos-friendly one:\n\n" "%%evt.num %%evt.outputtime %%evt.cpu %%mesos.task.name (%%container.id) %%proc.name (%%thread.tid:%%thread.vtid) %%evt.dir %%evt.type %%evt.info\n\n" "Examples:\n\n" " Capture all the events from the live system and print them to screen\n" " $ sysdig\n\n" " Capture all the events from the live system and save them to disk\n" " $ sysdig -w dumpfile.scap\n\n" " Read events from a file and print them to screen\n" " $ sysdig -r dumpfile.scap\n\n" " Print all the open system calls invoked by cat\n" " $ sysdig proc.name=cat and evt.type=open\n\n" " Print the name of the files opened by cat\n" " $ sysdig -p\"%%evt.arg.name\" proc.name=cat and evt.type=open\n\n" ); } void print_summary_table(sinsp* inspector, vector &summary_table, uint32_t nentries) { sinsp_evttables* einfo = inspector->get_event_info_tables(); cout << "----------------------\n"; string tstr = string("Event"); tstr.resize(16, ' '); tstr += "#Calls\n"; cout << tstr; cout << "----------------------\n"; sort(summary_table.begin(), summary_table.end(), summary_table_entry_rsort_comparer()); for(uint32_t j = 0; j < nentries; j++) { const summary_table_entry &e = summary_table.at(j); if(e.m_ncalls == 0) { break; } if(e.m_is_unsupported_syscall) { tstr = einfo->m_syscall_info_table[e.m_id / 2].name; tstr.resize(16, ' '); printf("%s%s%" PRIu64 "\n", (PPME_IS_ENTER(e.m_id))? "> ": "< ", tstr.c_str(), e.m_ncalls); } else { tstr = einfo->m_event_info[e.m_id].name; tstr.resize(16, ' '); printf("%s%s%" PRIu64 "\n", (PPME_IS_ENTER(e.m_id))? "> ": "< ", tstr.c_str(), e.m_ncalls); } } } #ifdef HAS_CHISELS static void add_chisel_dirs(sinsp* inspector) { // // Add the default chisel directory statically configured by the build system // inspector->add_chisel_dir(SYSDIG_INSTALLATION_DIR CHISELS_INSTALLATION_DIR, false); // // Add the directories configured in the SYSDIG_CHISEL_DIR environment variable // char* s_user_cdirs = getenv("SYSDIG_CHISEL_DIR"); if(s_user_cdirs != NULL) { vector user_cdirs = sinsp_split(s_user_cdirs, ';'); for(uint32_t j = 0; j < user_cdirs.size(); j++) { inspector->add_chisel_dir(user_cdirs[j], true); } } } #endif static void initialize_chisels() { #ifdef HAS_CHISELS for(uint32_t j = 0; j < g_chisels.size(); j++) { g_chisels[j]->on_init(); } #endif } // // Parse the command line following a chisel to consume the chisel command line. // We use the following strategy: // - if the chisel has no arguments, we don't consume anything // - if the chisel has at least one required argument, we consume the next command line token // - if the chisel has only optional arguments, we consume the next token, unless // - there is no next token // - the next token starts with a '-' // - the rest of the command line contains a valid filter // static void parse_chisel_args(sinsp_chisel* ch, sinsp* inspector, int optind, int argc, char **argv, int32_t* n_filterargs) { uint32_t nargs = ch->get_n_args(); uint32_t nreqargs = ch->get_n_required_args(); string args; if(nargs != 0) { if(optind > (int32_t)argc) { throw sinsp_exception("invalid number of arguments for chisel " + string(optarg) + ", " + to_string((long long int)nargs) + " expected."); } else if(optind < (int32_t)argc) { args = argv[optind]; if(nreqargs != 0) { ch->set_args(args); (*n_filterargs)++; } else { if(args[0] != '-') { string testflt; for(int32_t j = optind; j < argc; j++) { testflt += argv[j]; if(j < argc - 1) { testflt += " "; } } if(nargs == 1 && ch->get_lua_script_info()->m_args[0].m_type == "filter") { ch->set_args(args); (*n_filterargs)++; } else { try { sinsp_filter_compiler compiler(inspector, testflt); sinsp_filter* s = compiler.compile(); delete s; } catch(...) { ch->set_args(args); (*n_filterargs)++; } } } } } else { if(nreqargs != 0) { throw sinsp_exception("missing arguments for chisel " + string(optarg)); } } } } static void free_chisels() { #ifdef HAS_CHISELS for(vector::iterator it = g_chisels.begin(); it != g_chisels.end(); ++it) { delete *it; } g_chisels.clear(); #endif } static void chisels_on_capture_start() { #ifdef HAS_CHISELS for(uint32_t j = 0; j < g_chisels.size(); j++) { g_chisels[j]->on_capture_start(); } #endif } static void chisels_on_capture_end() { #ifdef HAS_CHISELS for(vector::iterator it = g_chisels.begin(); it != g_chisels.end(); ++it) { (*it)->on_capture_end(); } #endif } static void chisels_do_timeout(sinsp_evt* ev) { #ifdef HAS_CHISELS for(vector::iterator it = g_chisels.begin(); it != g_chisels.end(); ++it) { (*it)->do_timeout(ev); } #endif } void handle_end_of_file(bool print_progress, sinsp_evt_formatter* formatter = NULL) { string line; // Notify the formatter that we are at the // end of the capture in case it needs to // write any terminating characters if(formatter != NULL && formatter->on_capture_end(&line)) { cout << line << endl; } // // Reached the end of a trace file. // If we are reporting prgress, this is 100% // if(print_progress) { fprintf(stderr, "100.00\n"); fflush(stderr); } // // Notify the chisels that we're exiting. // try { chisels_on_capture_end(); } catch(...) { } } vector split_nextrun_args(string na) { vector res; uint32_t laststart = 0; uint32_t j; bool inquote = false; for(j = 0; j < na.size(); j++) { if(na[j] == '"') { inquote = !inquote; } else if(na[j] == ' ') { if(!inquote) { string arg = na.substr(laststart, j - laststart); replace_in_place(arg, "\"", ""); res.push_back(arg); laststart = j + 1; } } } res.push_back(na.substr(laststart, j - laststart)); laststart = j + 1; return res; } // // Event processing loop // captureinfo do_inspect(sinsp* inspector, uint64_t cnt, uint64_t duration_to_tot_ns, bool quiet, bool json, bool do_flush, bool print_progress, sinsp_filter* display_filter, vector &summary_table, sinsp_evt_formatter* formatter) { captureinfo retval; int32_t res; sinsp_evt* ev; string line; double last_printed_progress_pct = 0; uint64_t duration_start = 0; if(json) { do_flush = true; } // // Loop through the events // while(1) { if(retval.m_nevts == cnt || g_terminate) { // // End of capture, either because the user stopped it, or because // we reached the event count specified with -n. // handle_end_of_file(print_progress, formatter); break; } res = inspector->next(&ev); if(res == SCAP_TIMEOUT) { if(ev != NULL && ev->is_filtered_out()) { // // The event has been dropped by the filtering system. // Give the chisels a chance to run their timeout logic. // chisels_do_timeout(ev); } continue; } else if(res == SCAP_EOF) { handle_end_of_file(print_progress, formatter); break; } else if(res != SCAP_SUCCESS) { // // Event read error. // Notify the chisels that we're exiting, and then die with an error. // handle_end_of_file(print_progress, formatter); cerr << "res = " << res << endl; throw sinsp_exception(inspector->getlasterr().c_str()); } if (duration_start == 0) { duration_start = ev->get_ts(); } else if(duration_to_tot_ns > 0) { if(ev->get_ts() - duration_start >= duration_to_tot_ns) { handle_end_of_file(print_progress, formatter); break; } } retval.m_nevts++; if(print_progress) { if(ev->get_num() % 10000 == 0) { double progress_pct = inspector->get_read_progress(); if(progress_pct - last_printed_progress_pct > 0.1) { fprintf(stderr, "%.2lf\n", progress_pct); fflush(stderr); last_printed_progress_pct = progress_pct; } } } // // If there are chisels to run, run them // #ifdef HAS_CHISELS if(!g_chisels.empty()) { for(vector::iterator it = g_chisels.begin(); it != g_chisels.end(); ++it) { if((*it)->run(ev) == false) { continue; } } } else #endif { // // If we're supposed to summarize, increase the count for this event // if(!summary_table.empty()) { uint16_t etype = ev->get_type(); if(etype == PPME_GENERIC_E) { sinsp_evt_param *parinfo = ev->get_param(0); uint16_t id = *(int16_t *)parinfo->m_val; summary_table[PPM_EVENT_MAX + id * 2].m_ncalls++; } else if(etype == PPME_GENERIC_X) { sinsp_evt_param *parinfo = ev->get_param(0); uint16_t id = *(int16_t *)parinfo->m_val; summary_table[PPM_EVENT_MAX + id * 2 + 1].m_ncalls++; } else { summary_table[etype].m_ncalls++; } } // // When the quiet flag is specified, we don't do any kind of processing other // than counting the events. // if(quiet) { continue; } if(!inspector->is_debug_enabled() && ev->get_category() & EC_INTERNAL) { continue; } if(formatter->tostring(ev, &line)) { // // Output the line // if(display_filter) { if(!display_filter->run(ev)) { continue; } } cout << line << endl; } } if(do_flush) { cout << flush; } } return retval; } // // ARGUMENT PARSING AND PROGRAM SETUP // sysdig_init_res sysdig_init(int argc, char **argv) { sysdig_init_res res; sinsp* inspector = NULL; vector infiles; string outfile; int op; uint64_t cnt = -1; bool quiet = false; bool is_filter_display = false; bool verbose = false; bool list_flds = false; bool list_flds_markdown = false; bool print_progress = false; bool compress = false; sinsp_evt::param_fmt event_buffer_format = sinsp_evt::PF_NORMAL; sinsp_filter* display_filter = NULL; double duration = 1; int duration_to_tot = 0; captureinfo cinfo; string output_format; uint32_t snaplen = 0; int long_index = 0; int32_t n_filterargs = 0; bool jflag = false; bool unbuf_flag = false; bool filter_proclist_flag = false; string cname; vector summary_table; string* k8s_api = 0; string* k8s_api_cert = 0; string* mesos_api = 0; bool force_tracers_capture = false; bool page_faults = false; bool bpf = false; string bpf_probe; std::set suppress_comms; #ifdef HAS_CAPTURE string cri_socket_path; #endif // These variables are for the cycle_writer engine int duration_seconds = 0; int rollover_mb = 0; int file_limit = 0; unsigned long event_limit = 0L; static struct option long_options[] = { {"print-ascii", no_argument, 0, 'A' }, {"print-base64", no_argument, 0, 'b' }, {"bpf", optional_argument, 0, 'B' }, #ifdef HAS_CHISELS {"chisel", required_argument, 0, 'c' }, {"list-chisels", no_argument, 0, 0 }, #endif #ifdef HAS_CAPTURE {"cri", required_argument, 0, 0 }, {"cri-timeout", required_argument, 0, 0 }, #endif {"displayflt", no_argument, 0, 'd' }, {"debug", no_argument, 0, 'D'}, {"exclude-users", no_argument, 0, 'E' }, {"event-limit", required_argument, 0, 'e'}, {"fatfile", no_argument, 0, 'F'}, {"filter-proclist", no_argument, 0, 0 }, {"seconds", required_argument, 0, 'G' }, {"help", no_argument, 0, 'h' }, #ifdef HAS_CHISELS {"chisel-info", required_argument, 0, 'i' }, #endif {"file-size", required_argument, 0, 'C' }, {"json", no_argument, 0, 'j' }, {"k8s-api", required_argument, 0, 'k'}, {"k8s-api-cert", required_argument, 0, 'K' }, {"large-environment", no_argument, 0, 0 }, {"list", no_argument, 0, 'l' }, {"list-events", no_argument, 0, 'L' }, {"list-markdown", no_argument, 0, 0 }, {"mesos-api", required_argument, 0, 'm'}, {"numevents", required_argument, 0, 'n' }, {"page-faults", no_argument, 0, 0 }, {"progress", required_argument, 0, 'P' }, {"print", required_argument, 0, 'p' }, {"quiet", no_argument, 0, 'q' }, {"resolve-ports", no_argument, 0, 'R'}, {"readfile", required_argument, 0, 'r' }, {"snaplen", required_argument, 0, 's' }, {"summary", no_argument, 0, 'S' }, {"suppress-comm", required_argument, 0, 'U' }, {"timetype", required_argument, 0, 't' }, {"force-tracers-capture", required_argument, 0, 'T'}, {"unbuffered", no_argument, 0, 0 }, {"verbose", no_argument, 0, 'v' }, {"version", no_argument, 0, 0 }, {"writefile", required_argument, 0, 'w' }, {"limit", required_argument, 0, 'W' }, {"print-hex", no_argument, 0, 'x'}, {"print-hex-ascii", no_argument, 0, 'X'}, {"compress", no_argument, 0, 'z' }, {0, 0, 0, 0} }; output_format = "*%evt.num %evt.outputtime %evt.cpu %proc.name (%thread.tid) %evt.dir %evt.type %evt.info"; try { inspector = new sinsp(); inspector->set_hostname_and_port_resolution_mode(false); #ifdef HAS_CHISELS add_chisel_dirs(inspector); #endif // // Parse the args // while((op = getopt_long(argc, argv, "AbB::c:" "C:" "dDEe:F" "G:" "hi:jk:K:lLm:M:n:Pp:qRr:Ss:t:TU:v" "W:" "w:xXz", long_options, &long_index)) != -1) { switch(op) { case 'A': if(event_buffer_format != sinsp_evt::PF_NORMAL) { fprintf(stderr, "you cannot specify more than one output format\n"); delete inspector; return sysdig_init_res(EXIT_SUCCESS); } event_buffer_format = sinsp_evt::PF_EOLS; break; case 'b': if(event_buffer_format != sinsp_evt::PF_NORMAL) { fprintf(stderr, "you cannot specify more than one output format\n"); delete inspector; return sysdig_init_res(EXIT_SUCCESS); } event_buffer_format = sinsp_evt::PF_BASE64; break; case 'B': { bpf = true; if(optarg) { bpf_probe = optarg; } break; } #ifdef HAS_CHISELS case 'c': { string chisel = optarg; if(chisel == "l") { vector chlist; sinsp_chisel::get_chisel_list(&chlist); list_chisels(&chlist, true); delete inspector; return sysdig_init_res(EXIT_SUCCESS); } sinsp_chisel* ch = new sinsp_chisel(inspector, chisel); parse_chisel_args(ch, inspector, optind, argc, argv, &n_filterargs); g_chisels.push_back(ch); } #endif break; // File-size case 'C': rollover_mb = atoi(optarg); if(rollover_mb <= 0) { throw sinsp_exception(string("invalid file size") + optarg); res.m_res = EXIT_FAILURE; goto exit; } break; case 'D': inspector->set_debug_mode(true); inspector->set_internal_events_mode(true); inspector->set_log_stderr(); break; case 'E': inspector->set_import_users(false); break; case 'e': event_limit = strtoul(optarg, NULL, 0); if(event_limit <= 0) { throw sinsp_exception(string("invalid parameter 'number of events' ") + optarg); res.m_res = EXIT_FAILURE; goto exit; } break; case 'F': inspector->set_fatfile_dump_mode(true); break; // Number of seconds between roll-over case 'G': duration_seconds = atoi(optarg); if(duration_seconds <= 0) { throw sinsp_exception(string("invalid duration") + optarg); res.m_res = EXIT_FAILURE; goto exit; } break; #ifdef HAS_CHISELS // --chisel-info and -i case 'i': { cname = optarg; vector chlist; sinsp_chisel::get_chisel_list(&chlist); for(uint32_t j = 0; j < chlist.size(); j++) { if(chlist[j].m_name == cname) { print_chisel_info(&chlist[j]); delete inspector; return sysdig_init_res(EXIT_SUCCESS); } } throw sinsp_exception("chisel " + cname + " not found - use -cl to list them."); } break; #endif case 'd': is_filter_display = true; break; case 'j': // // set the json flag to 1 for now, the data format will depend from the print format parameters // jflag = true; break; case 'k': k8s_api = new string(optarg); break; case 'K': k8s_api_cert = new string(optarg); break; case 'h': usage(); delete inspector; return sysdig_init_res(EXIT_SUCCESS); case 'l': list_flds = true; break; case 'L': list_events(inspector); delete inspector; return sysdig_init_res(EXIT_SUCCESS); case 'm': mesos_api = new string(optarg); break; case 'M': duration_to_tot = atoi(optarg); if(duration_to_tot <= 0) { throw sinsp_exception(string("invalid duration") + optarg); res.m_res = EXIT_FAILURE; goto exit; } break; case 'n': try { cnt = sinsp_numparser::parseu64(optarg); } catch(...) { throw sinsp_exception("can't parse the -n argument, make sure it's a number"); } if(cnt <= 0) { throw sinsp_exception(string("invalid event count ") + optarg); res.m_res = EXIT_FAILURE; goto exit; } break; case 'P': print_progress = true; break; case 'p': if(string(optarg) == "p") { // -pp shows the default output format, useful if the user wants to tweak it. printf("%s\n", output_format.c_str()); delete inspector; return sysdig_init_res(EXIT_SUCCESS); } else if(string(optarg) == "c" || string(optarg) == "container") { output_format = "*%evt.num %evt.outputtime %evt.cpu %container.name (%container.id) %proc.name (%thread.tid:%thread.vtid) %evt.dir %evt.type %evt.info"; // This enables chisels to determine if they should print container information if(inspector != NULL) { inspector->set_print_container_data(true); } } else if(string(optarg) == "k" || string(optarg) == "kubernetes") { output_format = "*%evt.num %evt.outputtime %evt.cpu %k8s.pod.name (%container.id) %proc.name (%thread.tid:%thread.vtid) %evt.dir %evt.type %evt.info"; // This enables chisels to determine if they should print container information if(inspector != NULL) { inspector->set_print_container_data(true); } } else if(string(optarg) == "m" || string(optarg) == "mesos") { output_format = "*%evt.num %evt.outputtime %evt.cpu %mesos.task.name (%container.id) %proc.name (%thread.tid:%thread.vtid) %evt.dir %evt.type %evt.info"; // This enables chisels to determine if they should print container information if(inspector != NULL) { inspector->set_print_container_data(true); } } else { output_format = optarg; } break; case 'q': quiet = true; break; case 'R': inspector->set_hostname_and_port_resolution_mode(true); break; case 'r': infiles.push_back(optarg); k8s_api = new string(); mesos_api = new string(); break; case 'S': for(uint32_t j = 0; j < PPM_EVENT_MAX; j++) { summary_table.push_back(summary_table_entry(j, false)); } for(uint32_t j = 0; j < PPM_SC_MAX * 2; j++) { summary_table.push_back(summary_table_entry(j, true)); } break; case 's': snaplen = atoi(optarg); break; case 't': { string tms(optarg); if(tms == "h" || tms == "a" || tms == "r" || tms == "d" || tms == "D") { inspector->set_time_output_mode(tms.c_str()[0]); } else { fprintf(stderr, "invalid modifier for flag -t\n"); delete inspector; return sysdig_init_res(EXIT_FAILURE); } } break; case 'T': force_tracers_capture = true; break; case 'U': suppress_comms.insert(string(optarg)); break; case 'v': verbose = true; break; case 'w': outfile = optarg; quiet = true; break; // Number of capture files to cycle through case 'W': file_limit = atoi(optarg); if(file_limit <= 0) { throw sinsp_exception(string("invalid file limit") + optarg); res.m_res = EXIT_FAILURE; goto exit; } break; case 'x': if(event_buffer_format != sinsp_evt::PF_NORMAL) { fprintf(stderr, "you cannot specify more than one output format\n"); delete inspector; return sysdig_init_res(EXIT_FAILURE); } event_buffer_format = sinsp_evt::PF_HEX; break; case 'X': if(event_buffer_format != sinsp_evt::PF_NORMAL) { fprintf(stderr, "you cannot specify more than one output format\n"); delete inspector; return sysdig_init_res(EXIT_FAILURE); } event_buffer_format = sinsp_evt::PF_HEXASCII; break; case 'z': compress = true; break; case 0: { string optname = string(long_options[long_index].name); if (long_options[long_index].flag != 0) { break; } if (optname == "version") { printf("sysdig version %s\n", SYSDIG_VERSION); delete inspector; return sysdig_init_res(EXIT_SUCCESS); } else if (optname == "list-chisels") { vector chlist; sinsp_chisel::get_chisel_list(&chlist); list_chisels(&chlist, true); delete inspector; return sysdig_init_res(EXIT_SUCCESS); } #ifdef HAS_CAPTURE else if (optname == "cri") { cri_socket_path = optarg; } else if (optname == "cri-timeout") { inspector->set_cri_timeout(sinsp_numparser::parsed64(optarg)); } #endif else if (optname == "unbuffered") { unbuf_flag = true; } else if (optname == "filter-proclist") { filter_proclist_flag = true; } else if (optname == "large-environment") { inspector->set_large_envs(true); } else if (optname == "list-markdown") { list_flds = true; list_flds_markdown = true; } else if (optname == "page-faults") { page_faults = true; } } break; // getopt_long : '?' for an ambiguous match or an extraneous parameter case '?': delete inspector; return sysdig_init_res(EXIT_FAILURE); break; default: break; } } #ifdef HAS_CAPTURE if(!cri_socket_path.empty()) { inspector->set_cri_socket_path(cri_socket_path); } #endif if(!bpf) { const char *probe = scap_get_bpf_probe_from_env(); if(probe) { bpf = true; bpf_probe = probe; } } if(bpf) { inspector->set_bpf_probe(bpf_probe); } // // If -j was specified the event_buffer_format must be rewritten to account for it // if(jflag) { switch (event_buffer_format) { case sinsp_evt::PF_NORMAL: event_buffer_format = sinsp_evt::PF_JSON; break; case sinsp_evt::PF_EOLS: event_buffer_format = sinsp_evt::PF_JSONEOLS; break; case sinsp_evt::PF_HEX: event_buffer_format = sinsp_evt::PF_JSONHEX; break; case sinsp_evt::PF_HEXASCII: event_buffer_format = sinsp_evt::PF_JSONHEXASCII; break; case sinsp_evt::PF_BASE64: event_buffer_format = sinsp_evt::PF_JSONBASE64; break; default: // do nothing break; } } inspector->set_buffer_format(event_buffer_format); // // If -l was specified, print the fields and exit // if(list_flds) { if(verbose) { // // -ll shows the fields verbosely, i.e. with more information // like the type // list_fields(true, list_flds_markdown); } else { list_fields(false, list_flds_markdown); } res.m_res = EXIT_SUCCESS; goto exit; } string filter; // // the filter is at the end of the command line // if(optind + n_filterargs < argc) { #ifdef HAS_FILTERING for(int32_t j = optind + n_filterargs; j < argc; j++) { filter += argv[j]; if(j < argc - 1) { filter += " "; } } if(is_filter_display) { sinsp_filter_compiler compiler(inspector, filter); display_filter = compiler.compile(); } #else fprintf(stderr, "filtering not compiled.\n"); res.m_res = EXIT_FAILURE; goto exit; #endif } if(signal(SIGINT, signal_callback) == SIG_ERR) { fprintf(stderr, "An error occurred while setting SIGINT signal handler.\n"); res.m_res = EXIT_FAILURE; goto exit; } if(signal(SIGTERM, signal_callback) == SIG_ERR) { fprintf(stderr, "An error occurred while setting SIGTERM signal handler.\n"); res.m_res = EXIT_FAILURE; goto exit; } // // Create the event formatter // sinsp_evt_formatter formatter(inspector, output_format); // // Set output buffers len // if(!verbose && g_chisels.size() == 0) { inspector->set_max_evt_output_len(80); } // // Determine if we need to filter when dumping to file // if(filter_proclist_flag) { if(filter != "") { if(infiles.size() == 0) { fprintf(stderr, "--filter-proclist not supported with live captures.\n"); res.m_res = EXIT_FAILURE; goto exit; } inspector->filter_proc_table_when_saving(true); } else { fprintf(stderr, "you must specify a filter if you use --filter-proclist.\n"); res.m_res = EXIT_FAILURE; goto exit; } } for(uint32_t j = 0; j < infiles.size() || infiles.size() == 0; j++) { #ifdef HAS_FILTERING if(filter.size() && !is_filter_display) { inspector->set_filter(filter); } #endif // Suppress any comms specified via -U. We // need to do this *before* opening the // inspector, as that reads the process list. for(auto &comm : suppress_comms) { if (!inspector->suppress_events_comm(comm.c_str())) { fprintf(stderr, "Could not add %s to the set of suppressed comms--did you specify more than %d values?\n", comm.c_str(), SCAP_MAX_SUPPRESSED_COMMS); res.m_res = EXIT_FAILURE; goto exit; } } // // Launch the capture // if(infiles.size() != 0) { initialize_chisels(); // // We have a file to open // inspector->open(infiles[j]); } else { if(j > 0) { break; } initialize_chisels(); // // No file to open, this is a live capture // #if defined(HAS_CAPTURE) bool open_success = true; if(print_progress) { fprintf(stderr, "the -P flag cannot be used with live captures.\n"); res.m_res = EXIT_FAILURE; goto exit; } try { inspector->open(""); } catch(sinsp_exception e) { open_success = false; } // // Starting the live capture failed, try to load the driver with // modprobe. // if(!open_success) { open_success = true; if(bpf) { if(bpf_probe.empty()) { if(system("sysdig-probe-loader bpf")) { fprintf(stderr, "Unable to load the BPF probe\n"); } } } else { if(system("modprobe " PROBE_NAME " > /dev/null 2> /dev/null")) { fprintf(stderr, "Unable to load the driver\n"); } } inspector->open(""); } #else // // Starting live capture // If this fails on Windows and OSX, don't try with any driver // inspector->open(""); #endif // // Enable gathering the CPU from the kernel module // inspector->set_get_procs_cpu_from_driver(true); } // // If required, set the snaplen // if(snaplen != 0) { inspector->set_snaplen(snaplen); } // // If required, tell the driver to enable tracers capture // if(force_tracers_capture) { inspector->enable_tracers_capture(); } if(page_faults) { inspector->enable_page_faults(); } duration = ((double)clock()) / CLOCKS_PER_SEC; if(outfile != "") { inspector->setup_cycle_writer(outfile, rollover_mb, duration_seconds, file_limit, event_limit, compress); inspector->autodump_next_file(); } // // Notify the chisels that the capture is starting // chisels_on_capture_start(); // // run k8s, if required // if(k8s_api) { if(!k8s_api_cert) { if(char* k8s_cert_env = getenv("SYSDIG_K8S_API_CERT")) { k8s_api_cert = new string(k8s_cert_env); } } inspector->init_k8s_client(k8s_api, k8s_api_cert, verbose); k8s_api = 0; k8s_api_cert = 0; } else if(char* k8s_api_env = getenv("SYSDIG_K8S_API")) { if(k8s_api_env != NULL) { if(!k8s_api_cert) { if(char* k8s_cert_env = getenv("SYSDIG_K8S_API_CERT")) { k8s_api_cert = new string(k8s_cert_env); } } k8s_api = new string(k8s_api_env); inspector->init_k8s_client(k8s_api, k8s_api_cert, verbose); } else { delete k8s_api; delete k8s_api_cert; } k8s_api = 0; k8s_api_cert = 0; } // // run mesos, if required // if(mesos_api) { inspector->init_mesos_client(mesos_api, verbose); } else if(char* mesos_api_env = getenv("SYSDIG_MESOS_API")) { if(mesos_api_env != NULL) { mesos_api = new string(mesos_api_env); inspector->init_mesos_client(mesos_api, verbose); } } delete mesos_api; mesos_api = 0; cinfo = do_inspect(inspector, cnt, uint64_t(duration_to_tot*ONE_SECOND_IN_NS), quiet, jflag, unbuf_flag, print_progress, display_filter, summary_table, &formatter); duration = ((double)clock()) / CLOCKS_PER_SEC - duration; scap_stats cstats; inspector->get_capture_stats(&cstats); if(verbose) { fprintf(stderr, "Driver Events:%" PRIu64 "\nDriver Drops:%" PRIu64 "\nSuppressed by Comm:%" PRIu64 "\n", cstats.n_evts, cstats.n_drops, cstats.n_suppressed); fprintf(stderr, "Elapsed time: %.3lf, Captured Events: %" PRIu64 ", %.2lf eps\n", duration, cinfo.m_nevts, (double)cinfo.m_nevts / duration); } // // Done. Close the capture. // inspector->close(); } } catch(sinsp_capture_interrupt_exception&) { handle_end_of_file(print_progress); } catch(sinsp_exception& e) { cerr << e.what() << endl; handle_end_of_file(print_progress); res.m_res = e.scap_rc(); } catch(...) { handle_end_of_file(print_progress); res.m_res = EXIT_FAILURE; } exit: // // If any of the chisels is requesting another run, // for(vector::iterator it = g_chisels.begin(); it != g_chisels.end(); ++it) { string na; if((*it)->get_nextrun_args(&na)) { res.m_next_run_args = split_nextrun_args(na); } } // // If summary table is not empty, sort and print it // if(!summary_table.empty()) { print_summary_table(inspector, summary_table, 100); } // // Free all the stuff that was allocated // free_chisels(); if(inspector) { delete inspector; } if(display_filter) { delete display_filter; } return res; } // // MAIN // int main(int argc, char **argv) { sysdig_init_res res; res = sysdig_init(argc, argv); // // Check if a second run has been requested // if(res.m_next_run_args.size() != 0) { optind = 1; opterr = 1; optopt = '?'; int newargc = (int)res.m_next_run_args.size() + 1; vector newargv; newargv.push_back(argv[0]); for(int32_t j = 1; j < newargc; j++) { newargv.push_back((char*)res.m_next_run_args[j - 1].c_str()); } res = sysdig_init(newargc, &(newargv[0])); } #ifdef _WIN32 _CrtDumpMemoryLeaks(); #endif return res.m_res; } sysdig-0.26.4/userspace/sysdig/sysdig.h000066400000000000000000000036201352731327100200610ustar00rootroot00000000000000/* Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. 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. */ #pragma once #include #ifdef HAS_CAPTURE #include "driver_config.h" #endif // HAS_CAPTURE // // ASSERT implementation // #ifdef _DEBUG #define ASSERT(X) assert(X) #else // _DEBUG #define ASSERT(X) #endif // _DEBUG // // Capture results // class sysdig_init_res { public: sysdig_init_res() { m_res = EXIT_SUCCESS; } sysdig_init_res(int res) { m_res = res; } int m_res; vector m_next_run_args; }; // // Capture results // class captureinfo { public: captureinfo() { m_nevts = 0; m_time = 0; } uint64_t m_nevts; uint64_t m_time; }; // // Summary table entry // class summary_table_entry { public: summary_table_entry(uint16_t id, bool is_unsupported_syscall) : m_ncalls(0), m_id(id), m_is_unsupported_syscall(is_unsupported_syscall) { } uint64_t m_ncalls; uint16_t m_id; bool m_is_unsupported_syscall; }; struct summary_table_entry_rsort_comparer { bool operator() (const summary_table_entry& first, const summary_table_entry& second) const { return first.m_ncalls > second.m_ncalls; } }; // // Printer functions // void list_fields(bool verbose, bool markdown, bool names_only=false); void list_events(sinsp* inspector); #ifdef HAS_CHISELS void print_chisel_info(chisel_desc* cd); void list_chisels(vector* chlist, bool verbose); #endif sysdig-0.26.4/userspace/sysdig/sysdig.vcxproj000066400000000000000000000120411352731327100213220ustar00rootroot00000000000000 Debug Win32 Release Win32 {BD7F8F60-0014-4800-A6B3-ABD9A5A458FD} Win32Proj Application true v110 Application false v110 true ..\..\..\Debug\ ..\..\..\Debug\ true ..\..\..\Release\ ..\..\..\Release\ WIN32;PLATFORM_NAME="Windows";_DEBUG;_CONSOLE;_NO_DEBUG_HEAP;%(PreprocessorDefinitions) ./;../libsinsp;../libscap;../../common;%(AdditionalIncludeDirectories) MultiThreadedDebugDLL Level3 ProgramDatabase Disabled MachineX86 true Console kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Ws2_32.lib;$(ProjectDir)..\Debug\scap.lib;$(ProjectDir)..\Debug\sinsp.lib;%(AdditionalDependencies) ..\Debug\sysdig.exe WIN32;PLATFORM_NAME="Windows";NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ./;../libsinsp;../libscap;../../common;%(AdditionalIncludeDirectories) MultiThreadedDLL Level3 ProgramDatabase MachineX86 true Console true true kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Ws2_32.lib;..\Release\scap.lib;..\Release\sinsp.lib;%(AdditionalDependencies) ..\Release\sysdig.exe sysdig-0.26.4/userspace/sysdig/win32/000077500000000000000000000000001352731327100173475ustar00rootroot00000000000000sysdig-0.26.4/userspace/sysdig/win32/getopt.c000066400000000000000000000634131352731327100210240ustar00rootroot00000000000000/* Getopt for Microsoft C This code is a modification of the Free Software Foundation, Inc. Getopt library for parsing command line argument the purpose was to provide a Microsoft Visual C friendly derivative. This code provides functionality for both Unicode and Multibyte builds. Date: 02/03/2011 - Ludvik Jerabek - Initial Release Version: 1.0 Comment: Supports getopt, getopt_long, and getopt_long_only and POSIXLY_CORRECT environment flag License: LGPL Revisions: 02/03/2011 - Ludvik Jerabek - Initial Release 02/20/2011 - Ludvik Jerabek - Fixed compiler warnings at Level 4 07/05/2011 - Ludvik Jerabek - Added no_argument, required_argument, optional_argument defs 08/03/2011 - Ludvik Jerabek - Fixed non-argument runtime bug which caused runtime exception 08/09/2011 - Ludvik Jerabek - Added code to export functions for DLL and LIB 02/15/2012 - Ludvik Jerabek - Fixed _GETOPT_THROW definition missing in implementation file 08/01/2012 - Ludvik Jerabek - Created separate functions for char and wchar_t characters so single dll can do both unicode and ansi 10/15/2012 - Ludvik Jerabek - Modified to match latest GNU features **DISCLAIMER** THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT Not LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT APPLY TO YOU. IN NO EVENT WILL I BE LIABLE TO ANY PARTY FOR ANY DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY USE OF THIS MATERIAL INCLUDING, WITHOUT LIMITATION, ANY LOST PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. */ #include #include #include #include "getopt.h" #ifdef __cplusplus #define _GETOPT_THROW throw() #else #define _GETOPT_THROW #endif int optind = 1; int opterr = 1; int optopt = '?'; enum ENUM_ORDERING { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER }; // // // Ansi structures and functions follow // // static struct _getopt_data_a { int optind; int opterr; int optopt; char *optarg; int __initialized; char *__nextchar; enum ENUM_ORDERING __ordering; int __posixly_correct; int __first_nonopt; int __last_nonopt; } getopt_data_a; char *optarg_a; static void exchange_a(char **argv, struct _getopt_data_a *d) { int bottom = d->__first_nonopt; int middle = d->__last_nonopt; int top = d->optind; char *tem; while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { int len = middle - bottom; register int i; for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; } top -= len; } else { int len = top - middle; register int i; for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; } bottom += len; } } d->__first_nonopt += (d->optind - d->__last_nonopt); d->__last_nonopt = d->optind; } static const char *_getopt_initialize_a (const char *optstring, struct _getopt_data_a *d, int posixly_correct) { d->__first_nonopt = d->__last_nonopt = d->optind; d->__nextchar = NULL; d->__posixly_correct = posixly_correct | !!getenv("POSIXLY_CORRECT"); if (optstring[0] == '-') { d->__ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { d->__ordering = REQUIRE_ORDER; ++optstring; } else if (d->__posixly_correct) d->__ordering = REQUIRE_ORDER; else d->__ordering = PERMUTE; return optstring; } int _getopt_internal_r_a (int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, struct _getopt_data_a *d, int posixly_correct) { int print_errors = d->opterr; if (argc < 1) return -1; d->optarg = NULL; if (d->optind == 0 || !d->__initialized) { if (d->optind == 0) d->optind = 1; optstring = _getopt_initialize_a (optstring, d, posixly_correct); d->__initialized = 1; } else if (optstring[0] == '-' || optstring[0] == '+') optstring++; if (optstring[0] == ':') print_errors = 0; if (d->__nextchar == NULL || *d->__nextchar == '\0') { if (d->__last_nonopt > d->optind) d->__last_nonopt = d->optind; if (d->__first_nonopt > d->optind) d->__first_nonopt = d->optind; if (d->__ordering == PERMUTE) { if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind) exchange_a ((char **) argv, d); else if (d->__last_nonopt != d->optind) d->__first_nonopt = d->optind; while (d->optind < argc && (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0')) d->optind++; d->__last_nonopt = d->optind; } if (d->optind != argc && !strcmp(argv[d->optind], "--")) { d->optind++; if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind) exchange_a((char **) argv, d); else if (d->__first_nonopt == d->__last_nonopt) d->__first_nonopt = d->optind; d->__last_nonopt = argc; d->optind = argc; } if (d->optind == argc) { if (d->__first_nonopt != d->__last_nonopt) d->optind = d->__first_nonopt; return -1; } if ((argv[d->optind][0] != '-' || argv[d->optind][1] == '\0')) { if (d->__ordering == REQUIRE_ORDER) return -1; d->optarg = argv[d->optind++]; return 1; } d->__nextchar = (argv[d->optind] + 1 + (longopts != NULL && argv[d->optind][1] == '-')); } if (longopts != NULL && (argv[d->optind][1] == '-' || (long_only && (argv[d->optind][2] || !strchr(optstring, argv[d->optind][1]))))) { char *nameend; unsigned int namelen; const struct option_a *p; const struct option_a *pfound = NULL; struct option_list { const struct option_a *p; struct option_list *next; } *ambig_list = NULL; int exact = 0; int indfound = -1; int option_index; for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++); namelen = (unsigned int)(nameend - d->__nextchar); for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp(p->name, d->__nextchar, namelen)) { if (namelen == (unsigned int)strlen(p->name)) { pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { pfound = p; indfound = option_index; } else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) { struct option_list *newp = (struct option_list*)alloca(sizeof(*newp)); newp->p = p; newp->next = ambig_list; ambig_list = newp; } } if (ambig_list != NULL && !exact) { if (print_errors) { struct option_list first; first.p = pfound; first.next = ambig_list; ambig_list = &first; fprintf (stderr, "%s: option '%s' is ambiguous; possibilities:", argv[0], argv[d->optind]); do { fprintf (stderr, " '--%s'", ambig_list->p->name); ambig_list = ambig_list->next; } while (ambig_list != NULL); fputc ('\n', stderr); } d->__nextchar += strlen(d->__nextchar); d->optind++; d->optopt = 0; return '?'; } if (pfound != NULL) { option_index = indfound; d->optind++; if (*nameend) { if (pfound->has_arg) d->optarg = nameend + 1; else { if (print_errors) { if (argv[d->optind - 1][1] == '-') { fprintf(stderr, "%s: option '--%s' doesn't allow an argument\n",argv[0], pfound->name); } else { fprintf(stderr, "%s: option '%c%s' doesn't allow an argument\n",argv[0], argv[d->optind - 1][0],pfound->name); } } d->__nextchar += strlen(d->__nextchar); d->optopt = pfound->val; return '?'; } } else if (pfound->has_arg == 1) { if (d->optind < argc) d->optarg = argv[d->optind++]; else { if (print_errors) { fprintf(stderr,"%s: option '--%s' requires an argument\n",argv[0], pfound->name); } d->__nextchar += strlen(d->__nextchar); d->optopt = pfound->val; return optstring[0] == ':' ? ':' : '?'; } } d->__nextchar += strlen(d->__nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } if (!long_only || argv[d->optind][1] == '-' || strchr(optstring, *d->__nextchar) == NULL) { if (print_errors) { if (argv[d->optind][1] == '-') { fprintf(stderr, "%s: unrecognized option '--%s'\n",argv[0], d->__nextchar); } else { fprintf(stderr, "%s: unrecognized option '%c%s'\n",argv[0], argv[d->optind][0], d->__nextchar); } } d->__nextchar = (char *)""; d->optind++; d->optopt = 0; return '?'; } } { char c = *d->__nextchar++; char *temp = (char*)strchr(optstring, c); if (*d->__nextchar == '\0') ++d->optind; if (temp == NULL || c == ':' || c == ';') { if (print_errors) { fprintf(stderr, "%s: invalid option -- '%c'\n", argv[0], c); } d->optopt = c; return '?'; } if (temp[0] == 'W' && temp[1] == ';') { char *nameend; const struct option_a *p; const struct option_a *pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; if (longopts == NULL) goto no_longs; if (*d->__nextchar != '\0') { d->optarg = d->__nextchar; d->optind++; } else if (d->optind == argc) { if (print_errors) { fprintf(stderr,"%s: option requires an argument -- '%c'\n",argv[0], c); } d->optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; return c; } else d->optarg = argv[d->optind++]; for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '='; nameend++); for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp(p->name, d->__nextchar, nameend - d->__nextchar)) { if ((unsigned int) (nameend - d->__nextchar) == strlen(p->name)) { pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { pfound = p; indfound = option_index; } else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) ambig = 1; } if (ambig && !exact) { if (print_errors) { fprintf(stderr, "%s: option '-W %s' is ambiguous\n",argv[0], d->optarg); } d->__nextchar += strlen(d->__nextchar); d->optind++; return '?'; } if (pfound != NULL) { option_index = indfound; if (*nameend) { if (pfound->has_arg) d->optarg = nameend + 1; else { if (print_errors) { fprintf(stderr, "%s: option '-W %s' doesn't allow an argument\n",argv[0], pfound->name); } d->__nextchar += strlen(d->__nextchar); return '?'; } } else if (pfound->has_arg == 1) { if (d->optind < argc) d->optarg = argv[d->optind++]; else { if (print_errors) { fprintf(stderr, "%s: option '-W %s' requires an argument\n",argv[0], pfound->name); } d->__nextchar += strlen(d->__nextchar); return optstring[0] == ':' ? ':' : '?'; } } else d->optarg = NULL; d->__nextchar += strlen(d->__nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } no_longs: d->__nextchar = NULL; return 'W'; } if (temp[1] == ':') { if (temp[2] == ':') { if (*d->__nextchar != '\0') { d->optarg = d->__nextchar; d->optind++; } else d->optarg = NULL; d->__nextchar = NULL; } else { if (*d->__nextchar != '\0') { d->optarg = d->__nextchar; d->optind++; } else if (d->optind == argc) { if (print_errors) { fprintf(stderr,"%s: option requires an argument -- '%c'\n",argv[0], c); } d->optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; } else d->optarg = argv[d->optind++]; d->__nextchar = NULL; } } return c; } } int _getopt_internal_a (int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, int posixly_correct) { int result; getopt_data_a.optind = optind; getopt_data_a.opterr = opterr; result = _getopt_internal_r_a (argc, argv, optstring, longopts,longind, long_only, &getopt_data_a,posixly_correct); optind = getopt_data_a.optind; optarg_a = getopt_data_a.optarg; optopt = getopt_data_a.optopt; return result; } int getopt_a (int argc, char *const *argv, const char *optstring) _GETOPT_THROW { return _getopt_internal_a (argc, argv, optstring, (const struct option_a *) 0, (int *) 0, 0, 0); } int getopt_long_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW { return _getopt_internal_a (argc, argv, options, long_options, opt_index, 0, 0); } int getopt_long_only_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW { return _getopt_internal_a (argc, argv, options, long_options, opt_index, 1, 0); } int _getopt_long_r_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d) { return _getopt_internal_r_a (argc, argv, options, long_options, opt_index,0, d, 0); } int _getopt_long_only_r_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d) { return _getopt_internal_r_a (argc, argv, options, long_options, opt_index, 1, d, 0); } // // // Unicode Structures and Functions // // static struct _getopt_data_w { int optind; int opterr; int optopt; wchar_t *optarg; int __initialized; wchar_t *__nextchar; enum ENUM_ORDERING __ordering; int __posixly_correct; int __first_nonopt; int __last_nonopt; } getopt_data_w; wchar_t *optarg_w; static void exchange_w(wchar_t **argv, struct _getopt_data_w *d) { int bottom = d->__first_nonopt; int middle = d->__last_nonopt; int top = d->optind; wchar_t *tem; while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { int len = middle - bottom; register int i; for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; } top -= len; } else { int len = top - middle; register int i; for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; } bottom += len; } } d->__first_nonopt += (d->optind - d->__last_nonopt); d->__last_nonopt = d->optind; } static const wchar_t *_getopt_initialize_w (const wchar_t *optstring, struct _getopt_data_w *d, int posixly_correct) { d->__first_nonopt = d->__last_nonopt = d->optind; d->__nextchar = NULL; d->__posixly_correct = posixly_correct | !!_wgetenv(L"POSIXLY_CORRECT"); if (optstring[0] == L'-') { d->__ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == L'+') { d->__ordering = REQUIRE_ORDER; ++optstring; } else if (d->__posixly_correct) d->__ordering = REQUIRE_ORDER; else d->__ordering = PERMUTE; return optstring; } int _getopt_internal_r_w (int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, struct _getopt_data_w *d, int posixly_correct) { int print_errors = d->opterr; if (argc < 1) return -1; d->optarg = NULL; if (d->optind == 0 || !d->__initialized) { if (d->optind == 0) d->optind = 1; optstring = _getopt_initialize_w (optstring, d, posixly_correct); d->__initialized = 1; } else if (optstring[0] == L'-' || optstring[0] == L'+') optstring++; if (optstring[0] == L':') print_errors = 0; if (d->__nextchar == NULL || *d->__nextchar == L'\0') { if (d->__last_nonopt > d->optind) d->__last_nonopt = d->optind; if (d->__first_nonopt > d->optind) d->__first_nonopt = d->optind; if (d->__ordering == PERMUTE) { if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind) exchange_w((wchar_t **) argv, d); else if (d->__last_nonopt != d->optind) d->__first_nonopt = d->optind; while (d->optind < argc && (argv[d->optind][0] != L'-' || argv[d->optind][1] == L'\0')) d->optind++; d->__last_nonopt = d->optind; } if (d->optind != argc && !wcscmp(argv[d->optind], L"--")) { d->optind++; if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind) exchange_w((wchar_t **) argv, d); else if (d->__first_nonopt == d->__last_nonopt) d->__first_nonopt = d->optind; d->__last_nonopt = argc; d->optind = argc; } if (d->optind == argc) { if (d->__first_nonopt != d->__last_nonopt) d->optind = d->__first_nonopt; return -1; } if ((argv[d->optind][0] != L'-' || argv[d->optind][1] == L'\0')) { if (d->__ordering == REQUIRE_ORDER) return -1; d->optarg = argv[d->optind++]; return 1; } d->__nextchar = (argv[d->optind] + 1 + (longopts != NULL && argv[d->optind][1] == L'-')); } if (longopts != NULL && (argv[d->optind][1] == L'-' || (long_only && (argv[d->optind][2] || !wcschr(optstring, argv[d->optind][1]))))) { wchar_t *nameend; unsigned int namelen; const struct option_w *p; const struct option_w *pfound = NULL; struct option_list { const struct option_w *p; struct option_list *next; } *ambig_list = NULL; int exact = 0; int indfound = -1; int option_index; for (nameend = d->__nextchar; *nameend && *nameend != L'='; nameend++); namelen = (unsigned int)(nameend - d->__nextchar); for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!wcsncmp(p->name, d->__nextchar, namelen)) { if (namelen == (unsigned int)wcslen(p->name)) { pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { pfound = p; indfound = option_index; } else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) { struct option_list *newp = (struct option_list*)alloca(sizeof(*newp)); newp->p = p; newp->next = ambig_list; ambig_list = newp; } } if (ambig_list != NULL && !exact) { if (print_errors) { struct option_list first; first.p = pfound; first.next = ambig_list; ambig_list = &first; fwprintf(stderr, L"%s: option '%s' is ambiguous; possibilities:", argv[0], argv[d->optind]); do { fwprintf (stderr, L" '--%s'", ambig_list->p->name); ambig_list = ambig_list->next; } while (ambig_list != NULL); fputwc (L'\n', stderr); } d->__nextchar += wcslen(d->__nextchar); d->optind++; d->optopt = 0; return L'?'; } if (pfound != NULL) { option_index = indfound; d->optind++; if (*nameend) { if (pfound->has_arg) d->optarg = nameend + 1; else { if (print_errors) { if (argv[d->optind - 1][1] == L'-') { fwprintf(stderr, L"%s: option '--%s' doesn't allow an argument\n",argv[0], pfound->name); } else { fwprintf(stderr, L"%s: option '%c%s' doesn't allow an argument\n",argv[0], argv[d->optind - 1][0],pfound->name); } } d->__nextchar += wcslen(d->__nextchar); d->optopt = pfound->val; return L'?'; } } else if (pfound->has_arg == 1) { if (d->optind < argc) d->optarg = argv[d->optind++]; else { if (print_errors) { fwprintf(stderr,L"%s: option '--%s' requires an argument\n",argv[0], pfound->name); } d->__nextchar += wcslen(d->__nextchar); d->optopt = pfound->val; return optstring[0] == L':' ? L':' : L'?'; } } d->__nextchar += wcslen(d->__nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } if (!long_only || argv[d->optind][1] == L'-' || wcschr(optstring, *d->__nextchar) == NULL) { if (print_errors) { if (argv[d->optind][1] == L'-') { fwprintf(stderr, L"%s: unrecognized option '--%s'\n",argv[0], d->__nextchar); } else { fwprintf(stderr, L"%s: unrecognized option '%c%s'\n",argv[0], argv[d->optind][0], d->__nextchar); } } d->__nextchar = (wchar_t *)L""; d->optind++; d->optopt = 0; return L'?'; } } { wchar_t c = *d->__nextchar++; wchar_t *temp = (wchar_t*)wcschr(optstring, c); if (*d->__nextchar == L'\0') ++d->optind; if (temp == NULL || c == L':' || c == L';') { if (print_errors) { fwprintf(stderr, L"%s: invalid option -- '%c'\n", argv[0], c); } d->optopt = c; return L'?'; } if (temp[0] == L'W' && temp[1] == L';') { wchar_t *nameend; const struct option_w *p; const struct option_w *pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; if (longopts == NULL) goto no_longs; if (*d->__nextchar != L'\0') { d->optarg = d->__nextchar; d->optind++; } else if (d->optind == argc) { if (print_errors) { fwprintf(stderr,L"%s: option requires an argument -- '%c'\n",argv[0], c); } d->optopt = c; if (optstring[0] == L':') c = L':'; else c = L'?'; return c; } else d->optarg = argv[d->optind++]; for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != L'='; nameend++); for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!wcsncmp(p->name, d->__nextchar, nameend - d->__nextchar)) { if ((unsigned int) (nameend - d->__nextchar) == wcslen(p->name)) { pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { pfound = p; indfound = option_index; } else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) ambig = 1; } if (ambig && !exact) { if (print_errors) { fwprintf(stderr, L"%s: option '-W %s' is ambiguous\n",argv[0], d->optarg); } d->__nextchar += wcslen(d->__nextchar); d->optind++; return L'?'; } if (pfound != NULL) { option_index = indfound; if (*nameend) { if (pfound->has_arg) d->optarg = nameend + 1; else { if (print_errors) { fwprintf(stderr, L"%s: option '-W %s' doesn't allow an argument\n",argv[0], pfound->name); } d->__nextchar += wcslen(d->__nextchar); return L'?'; } } else if (pfound->has_arg == 1) { if (d->optind < argc) d->optarg = argv[d->optind++]; else { if (print_errors) { fwprintf(stderr, L"%s: option '-W %s' requires an argument\n",argv[0], pfound->name); } d->__nextchar += wcslen(d->__nextchar); return optstring[0] == L':' ? L':' : L'?'; } } else d->optarg = NULL; d->__nextchar += wcslen(d->__nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } no_longs: d->__nextchar = NULL; return L'W'; } if (temp[1] == L':') { if (temp[2] == L':') { if (*d->__nextchar != L'\0') { d->optarg = d->__nextchar; d->optind++; } else d->optarg = NULL; d->__nextchar = NULL; } else { if (*d->__nextchar != L'\0') { d->optarg = d->__nextchar; d->optind++; } else if (d->optind == argc) { if (print_errors) { fwprintf(stderr,L"%s: option requires an argument -- '%c'\n",argv[0], c); } d->optopt = c; if (optstring[0] == L':') c = L':'; else c = L'?'; } else d->optarg = argv[d->optind++]; d->__nextchar = NULL; } } return c; } } int _getopt_internal_w (int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, int posixly_correct) { int result; getopt_data_w.optind = optind; getopt_data_w.opterr = opterr; result = _getopt_internal_r_w (argc, argv, optstring, longopts,longind, long_only, &getopt_data_w,posixly_correct); optind = getopt_data_w.optind; optarg_w = getopt_data_w.optarg; optopt = getopt_data_w.optopt; return result; } int getopt_w (int argc, wchar_t *const *argv, const wchar_t *optstring) _GETOPT_THROW { return _getopt_internal_w (argc, argv, optstring, (const struct option_w *) 0, (int *) 0, 0, 0); } int getopt_long_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW { return _getopt_internal_w (argc, argv, options, long_options, opt_index, 0, 0); } int getopt_long_only_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW { return _getopt_internal_w (argc, argv, options, long_options, opt_index, 1, 0); } int _getopt_long_r_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d) { return _getopt_internal_r_w (argc, argv, options, long_options, opt_index,0, d, 0); } int _getopt_long_only_r_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d) { return _getopt_internal_r_w (argc, argv, options, long_options, opt_index, 1, d, 0); }sysdig-0.26.4/userspace/sysdig/win32/getopt.h000066400000000000000000000114461352731327100210300ustar00rootroot00000000000000/* Getopt for Microsoft C This code is a modification of the Free Software Foundation, Inc. Getopt library for parsing command line argument the purpose was to provide a Microsoft Visual C friendly derivative. This code provides functionality for both Unicode and Multibyte builds. Date: 02/03/2011 - Ludvik Jerabek - Initial Release Version: 1.0 Comment: Supports getopt, getopt_long, and getopt_long_only and POSIXLY_CORRECT environment flag License: LGPL Revisions: 02/03/2011 - Ludvik Jerabek - Initial Release 02/20/2011 - Ludvik Jerabek - Fixed compiler warnings at Level 4 07/05/2011 - Ludvik Jerabek - Added no_argument, required_argument, optional_argument defs 08/03/2011 - Ludvik Jerabek - Fixed non-argument runtime bug which caused runtime exception 08/09/2011 - Ludvik Jerabek - Added code to export functions for DLL and LIB 02/15/2012 - Ludvik Jerabek - Fixed _GETOPT_THROW definition missing in implementation file 08/01/2012 - Ludvik Jerabek - Created separate functions for char and wchar_t characters so single dll can do both unicode and ansi 10/15/2012 - Ludvik Jerabek - Modified to match latest GNU features **DISCLAIMER** THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT Not LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT APPLY TO YOU. IN NO EVENT WILL I BE LIABLE TO ANY PARTY FOR ANY DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY USE OF THIS MATERIAL INCLUDING, WITHOUT LIMITATION, ANY LOST PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef __GETOPT_H_ #define __GETOPT_H_ #ifdef _GETOPT_API #undef _GETOPT_API #endif #define STATIC_GETOPT #if defined(EXPORTS_GETOPT) && defined(STATIC_GETOPT) #error "The preprocessor definitions of EXPORTS_GETOPT and STATIC_GETOPT can only be used individually" #elif defined(STATIC_GETOPT) #pragma message("Warning static builds of getopt violate the Lesser GNU Public License") #define _GETOPT_API #elif defined(EXPORTS_GETOPT) #pragma message("Exporting getopt library") #define _GETOPT_API __declspec(dllexport) #else #pragma message("Importing getopt library") #define _GETOPT_API __declspec(dllimport) #endif // Change behavior for C\C++ #ifdef __cplusplus #define _BEGIN_EXTERN_C extern "C" { #define _END_EXTERN_C } #define _GETOPT_THROW throw() #else #define _BEGIN_EXTERN_C #define _END_EXTERN_C #define _GETOPT_THROW #endif // Standard GNU options #define null_argument 0 /*Argument Null*/ #define no_argument 0 /*Argument Switch Only*/ #define required_argument 1 /*Argument Required*/ #define optional_argument 2 /*Argument Optional*/ // Shorter Options #define ARG_NULL 0 /*Argument Null*/ #define ARG_NONE 0 /*Argument Switch Only*/ #define ARG_REQ 1 /*Argument Required*/ #define ARG_OPT 2 /*Argument Optional*/ #include #include _BEGIN_EXTERN_C extern _GETOPT_API int optind; extern _GETOPT_API int opterr; extern _GETOPT_API int optopt; // Ansi struct option_a { const char* name; int has_arg; int *flag; char val; }; extern _GETOPT_API char *optarg_a; extern _GETOPT_API int getopt_a(int argc, char *const *argv, const char *optstring) _GETOPT_THROW; extern _GETOPT_API int getopt_long_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW; extern _GETOPT_API int getopt_long_only_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW; // Unicode struct option_w { const wchar_t* name; int has_arg; int *flag; wchar_t val; }; extern _GETOPT_API wchar_t *optarg_w; extern _GETOPT_API int getopt_w(int argc, wchar_t *const *argv, const wchar_t *optstring) _GETOPT_THROW; extern _GETOPT_API int getopt_long_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW; extern _GETOPT_API int getopt_long_only_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW; _END_EXTERN_C #undef _BEGIN_EXTERN_C #undef _END_EXTERN_C #undef _GETOPT_THROW #undef _GETOPT_API #ifdef _UNICODE #define getopt getopt_w #define getopt_long getopt_long_w #define getopt_long_only getopt_long_only_w #define option option_w #define optarg optarg_w #else #define getopt getopt_a #define getopt_long getopt_long_a #define getopt_long_only getopt_long_only_a #define option option_a #define optarg optarg_a #endif #endif // __GETOPT_H_ sysdig-0.26.4/userspace/userspace.sln000066400000000000000000000041461352731327100176200ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sinsp", "libsinsp\sinsp.vcxproj", "{A45C552E-F4BA-480A-A7D3-EB1E9DDFBE6C}" ProjectSection(ProjectDependencies) = postProject {A45C5520-F4BA-480A-A7D3-EB1E9DDFBE6C} = {A45C5520-F4BA-480A-A7D3-EB1E9DDFBE6C} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scap", "libscap\scap.vcxproj", "{A45C5520-F4BA-480A-A7D3-EB1E9DDFBE6C}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sysdig", "sysdig\sysdig.vcxproj", "{BD7F8F60-0014-4800-A6B3-ABD9A5A458FD}" ProjectSection(ProjectDependencies) = postProject {A45C5520-F4BA-480A-A7D3-EB1E9DDFBE6C} = {A45C5520-F4BA-480A-A7D3-EB1E9DDFBE6C} {A45C552E-F4BA-480A-A7D3-EB1E9DDFBE6C} = {A45C552E-F4BA-480A-A7D3-EB1E9DDFBE6C} EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A45C552E-F4BA-480A-A7D3-EB1E9DDFBE6C}.Debug|Win32.ActiveCfg = Debug|Win32 {A45C552E-F4BA-480A-A7D3-EB1E9DDFBE6C}.Debug|Win32.Build.0 = Debug|Win32 {A45C552E-F4BA-480A-A7D3-EB1E9DDFBE6C}.Release|Win32.ActiveCfg = Release|Win32 {A45C552E-F4BA-480A-A7D3-EB1E9DDFBE6C}.Release|Win32.Build.0 = Release|Win32 {A45C5520-F4BA-480A-A7D3-EB1E9DDFBE6C}.Debug|Win32.ActiveCfg = Debug|Win32 {A45C5520-F4BA-480A-A7D3-EB1E9DDFBE6C}.Debug|Win32.Build.0 = Debug|Win32 {A45C5520-F4BA-480A-A7D3-EB1E9DDFBE6C}.Release|Win32.ActiveCfg = Release|Win32 {A45C5520-F4BA-480A-A7D3-EB1E9DDFBE6C}.Release|Win32.Build.0 = Release|Win32 {BD7F8F60-0014-4800-A6B3-ABD9A5A458FD}.Debug|Win32.ActiveCfg = Debug|Win32 {BD7F8F60-0014-4800-A6B3-ABD9A5A458FD}.Debug|Win32.Build.0 = Debug|Win32 {BD7F8F60-0014-4800-A6B3-ABD9A5A458FD}.Release|Win32.ActiveCfg = Release|Win32 {BD7F8F60-0014-4800-A6B3-ABD9A5A458FD}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal