pax_global_header00006660000000000000000000000064131653715160014522gustar00rootroot0000000000000052 comment=16ef7df182af5c91cfc26b4da8603b5446e9b389 sysdig-0.19.1/000077500000000000000000000000001316537151600131145ustar00rootroot00000000000000sysdig-0.19.1/.gitignore000066400000000000000000000002771316537151600151120ustar00rootroot00000000000000*.o *.ko *.ko.unsigned *.cmd *.symvers *.order *.mod *.mod.c *.o.d *.o.rc build/ driver/Makefile driver/driver_config.h *.pyc *.config *.creator *.creator.user* *.files *.includes *.pro.user sysdig-0.19.1/.travis-scripts/000077500000000000000000000000001316537151600161675ustar00rootroot00000000000000sysdig-0.19.1/.travis-scripts/linux/000077500000000000000000000000001316537151600173265ustar00rootroot00000000000000sysdig-0.19.1/.travis-scripts/linux/before_install.sh000077500000000000000000000001261316537151600226540ustar00rootroot00000000000000#!/bin/bash sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test sudo apt-get updatesysdig-0.19.1/.travis-scripts/linux/build.sh000077500000000000000000000032001316537151600207570ustar00rootroot00000000000000#!/bin/bash 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 rm -rf build pushd $(mktemp -d --tmpdir sysdig.XXXXXXXXXX) wget http://download.draios.com/dependencies/zlib-1.2.8.tar.gz tar -xzf zlib-1.2.8.tar.gz cd zlib-1.2.8 ./configure make sudo make install cd .. wget https://github.com/open-source-parsers/jsoncpp/archive/0.10.5.tar.gz tar zxvf 0.10.5.tar.gz cd jsoncpp-0.10.5 cmake -DBUILD_SHARED_LIBS=ON . make sudo make install cd .. wget https://s3.amazonaws.com/download.draios.com/dependencies/libb64-1.2.src.zip unzip libb64-1.2.src.zip cd libb64-1.2 make sudo cp -r include/* /usr/local/include/ sudo cp src/libb64.a /usr/local/lib/ cd .. wget http://download.draios.com/dependencies/jq-1.5.tar.gz tar -xzf jq-1.5.tar.gz cd jq-1.5 ./configure --disable-maintainer-mode make LDFLAGS=-all-static sudo cp ./jq.h /usr/local/include/ sudo cp ./jv.h /usr/local/include/ sudo cp .libs/libjq.a /usr/local/lib/ cd .. popd rm -rf userspace/libsinsp/third-party/jsoncpp sudo apt-get install libncurses5-dev libluajit-5.1-dev libcurl4-openssl-dev libssl-dev mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DUSE_BUNDLED_DEPS=OFF make VERBOSE=1 make package cd .. test/sysdig_trace_regression.sh build/userspace/sysdig/sysdig build/userspace/sysdig/chisels $TRAVIS_BRANCHsysdig-0.19.1/.travis-scripts/linux/install.sh000077500000000000000000000002601316537151600213310ustar00rootroot00000000000000#!/bin/bash sudo apt-get --force-yes install g++-4.8 sudo apt-get install rpm linux-headers-$(uname -r) sudo apt-get purge libncurses5-dev cmake libcurl4-openssl-dev zlib1g-devsysdig-0.19.1/.travis-scripts/osx/000077500000000000000000000000001316537151600170005ustar00rootroot00000000000000sysdig-0.19.1/.travis-scripts/osx/before_install.sh000077500000000000000000000000271316537151600223260ustar00rootroot00000000000000#!/bin/bash brew updatesysdig-0.19.1/.travis-scripts/osx/build.sh000077500000000000000000000003511316537151600204350ustar00rootroot00000000000000#!/bin/bash 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_BRANCHsysdig-0.19.1/.travis-scripts/osx/install.sh000077500000000000000000000004131316537151600210030ustar00rootroot00000000000000#!/bin/bash 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.19.1/.travis.yml000066400000000000000000000010461316537151600152260ustar00rootroot00000000000000language: 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.19.1/CMakeCPackOptions.cmake000066400000000000000000000001451316537151600173540ustar00rootroot00000000000000if(CPACK_GENERATOR MATCHES "TGZ") set(CPACK_SET_DESTDIR "ON") set(CPACK_STRIP_FILES "OFF") endif() sysdig-0.19.1/CMakeLists.txt000066400000000000000000000324741316537151600156660ustar00rootroot00000000000000# 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) 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) if(NOT WIN32) set(SYSDIG_DEBUG_FLAGS "-D_DEBUG") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -ggdb") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -ggdb --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() set(PROBE_VERSION "${SYSDIG_VERSION}") set(PROBE_NAME "sysdig-probe") set(PROBE_DEVICE_NAME "sysdig") 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.8.tar.gz" URL_MD5 "44d667c142d7cda120332623eab69f40" 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.8.tar.gz" URL_MD5 "44d667c142d7cda120332623eab69f40" CONFIGURE_COMMAND "" BUILD_COMMAND nmake -f win32/Makefile.msc BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${ZLIB_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() # # 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.2j.tar.gz" URL_MD5 "96322138f0b69e61b7212bc53d5e912b" 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.52.1.tar.bz2" URL_MD5 "dd014df06ff1d12e173de86873f9f77a" CONFIGURE_COMMAND ./configure ${CURL_SSL_OPTION} --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-nghttp2 --without-libssh2 BUILD_COMMAND ${CMD_MAKE} BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${CURL_LIBRARIES} INSTALL_COMMAND "") endif() endif() # NOT WIN32 AND NOT APPLE 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 "GPLv2") 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.19.1/COPYING000066400000000000000000000444311316537151600141550ustar00rootroot00000000000000 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. * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. 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.19.1/README.md000066400000000000000000000162241316537151600144000ustar00rootroot00000000000000sysdig ====== [![Build Status](https://travis-ci.org/draios/sysdig.png?branch=master)](https://travis-ci.org/draios/sysdig) [![Join the chat at https://gitter.im/draios/sysdig](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/draios/sysdig?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) # 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](http://www.sysdig.org/install/) - 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 --- Sysdig is licensed to you under the [GPL 2.0](https://github.com/draios/sysdig/blob/dev/COPYING) open source license. 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.19.1/cla/000077500000000000000000000000001316537151600136535ustar00rootroot00000000000000sysdig-0.19.1/cla/sysdig_contributor_agreement.txt000066400000000000000000000151121316537151600223770ustar00rootroot00000000000000DRAIOS, 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.19.1/cla/sysdig_corp_contributor_agreement.txt000066400000000000000000000171161316537151600234300ustar00rootroot00000000000000DRAIOS, 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.19.1/cla/sysdig_govt_contributor_agreement.txt000066400000000000000000000157231316537151600234460ustar00rootroot00000000000000DRAIOS, 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.19.1/coding_conventions.md000066400000000000000000000142621316537151600173330ustar00rootroot000000000000000 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.19.1/common/000077500000000000000000000000001316537151600144045ustar00rootroot00000000000000sysdig-0.19.1/common/inttypes_win.h000066400000000000000000000173021316537151600173140ustar00rootroot00000000000000// 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.19.1/conf/000077500000000000000000000000001316537151600140415ustar00rootroot00000000000000sysdig-0.19.1/conf/dev.conf000066400000000000000000000000511316537151600154620ustar00rootroot00000000000000export SYSDIG_VERSION=0.99.$BUILD_NUMBER sysdig-0.19.1/docker/000077500000000000000000000000001316537151600143635ustar00rootroot00000000000000sysdig-0.19.1/docker/dev/000077500000000000000000000000001316537151600151415ustar00rootroot00000000000000sysdig-0.19.1/docker/dev/Dockerfile000066400000000000000000000034041316537151600171340ustar00rootroot00000000000000FROM 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 echo "deb http://httpredir.debian.org/debian jessie main" > /etc/apt/sources.list.d/jessie.list \ && apt-get update \ && apt-get install -y --no-install-recommends \ bash-completion \ curl \ gnupg2 \ ca-certificates \ gcc \ gcc-5 \ gcc-4.9 \ libelf1 \ less \ procps \ && rm -rf /var/lib/apt/lists/* # Since our base Debian image ships with GCC 5.0 which breaks older kernels, revert the # default to gcc-4.9. Also, since some customers use some very old distributions whose kernel # makefile is hardcoded for gcc-4.6 or so (e.g. Debian Wheezy), we pretend to have gcc 4.6/4.7 # by symlinking it to 4.9 RUN rm -rf /usr/bin/gcc \ && ln -s /usr/bin/gcc-4.9 /usr/bin/gcc \ && ln -s /usr/bin/gcc-4.9 /usr/bin/gcc-4.8 \ && ln -s /usr/bin/gcc-4.9 /usr/bin/gcc-4.7 \ && ln -s /usr/bin/gcc-4.9 /usr/bin/gcc-4.6 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/* RUN ln -s $SYSDIG_HOST_ROOT/lib/modules /lib/modules COPY ./docker-entrypoint.sh / ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["bash"] sysdig-0.19.1/docker/dev/docker-entrypoint.sh000077500000000000000000000003161316537151600211600ustar00rootroot00000000000000#!/bin/bash #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.19.1/docker/local/000077500000000000000000000000001316537151600154555ustar00rootroot00000000000000sysdig-0.19.1/docker/local/Dockerfile000066400000000000000000000030241316537151600174460ustar00rootroot00000000000000FROM 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 echo "deb http://httpredir.debian.org/debian jessie main" > /etc/apt/sources.list.d/jessie.list \ && apt-get update \ && apt-get install -y --no-install-recommends \ bash-completion \ curl \ dkms \ gnupg2 \ ca-certificates \ gcc \ gcc-5 \ gcc-4.9 \ libelf1 \ less \ procps \ && rm -rf /var/lib/apt/lists/* # Since our base Debian image ships with GCC 5.0 which breaks older kernels, revert the # default to gcc-4.9. Also, since some customers use some very old distributions whose kernel # makefile is hardcoded for gcc-4.6 or so (e.g. Debian Wheezy), we pretend to have gcc 4.6/4.7 # by symlinking it to 4.9 RUN rm -rf /usr/bin/gcc \ && ln -s /usr/bin/gcc-4.9 /usr/bin/gcc \ && ln -s /usr/bin/gcc-4.9 /usr/bin/gcc-4.8 \ && ln -s /usr/bin/gcc-4.9 /usr/bin/gcc-4.7 \ && ln -s /usr/bin/gcc-4.9 /usr/bin/gcc-4.6 RUN 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 COPY ./docker-entrypoint.sh / ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["bash"] sysdig-0.19.1/docker/local/docker-entrypoint.sh000077500000000000000000000003151316537151600214730ustar00rootroot00000000000000#!/bin/bash #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.19.1/docker/stable/000077500000000000000000000000001316537151600156355ustar00rootroot00000000000000sysdig-0.19.1/docker/stable/Dockerfile000066400000000000000000000034071316537151600176330ustar00rootroot00000000000000FROM 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 echo "deb http://httpredir.debian.org/debian jessie main" > /etc/apt/sources.list.d/jessie.list \ && apt-get update \ && apt-get install -y --no-install-recommends \ bash-completion \ curl \ gnupg2 \ ca-certificates \ gcc \ gcc-5 \ gcc-4.9 \ libelf1 \ less \ procps \ && rm -rf /var/lib/apt/lists/* # Since our base Debian image ships with GCC 5.0 which breaks older kernels, revert the # default to gcc-4.9. Also, since some customers use some very old distributions whose kernel # makefile is hardcoded for gcc-4.6 or so (e.g. Debian Wheezy), we pretend to have gcc 4.6/4.7 # by symlinking it to 4.9 RUN rm -rf /usr/bin/gcc \ && ln -s /usr/bin/gcc-4.9 /usr/bin/gcc \ && ln -s /usr/bin/gcc-4.9 /usr/bin/gcc-4.8 \ && ln -s /usr/bin/gcc-4.9 /usr/bin/gcc-4.7 \ && ln -s /usr/bin/gcc-4.9 /usr/bin/gcc-4.6 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/* RUN ln -s $SYSDIG_HOST_ROOT/lib/modules /lib/modules COPY ./docker-entrypoint.sh / ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["bash"] sysdig-0.19.1/docker/stable/docker-entrypoint.sh000077500000000000000000000003161316537151600216540ustar00rootroot00000000000000#!/bin/bash #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.19.1/driver/000077500000000000000000000000001316537151600144075ustar00rootroot00000000000000sysdig-0.19.1/driver/CMakeLists.txt000066400000000000000000000047561316537151600171630ustar00rootroot00000000000000option(BUILD_DRIVER "Build the driver on Linux" ON) option(ENABLE_DKMS "Enable DKMS on Linux" ON) configure_file(dkms.conf.in dkms.conf) configure_file(Makefile.in Makefile.dkms) configure_file(driver_config.h.in driver_config.h) configure_file(driver_config.h.in "${CMAKE_CURRENT_SOURCE_DIR}/driver_config.h") set(CLEAN_FILES "${CMAKE_CURRENT_SOURCE_DIR}/Makefile" "${CMAKE_CURRENT_SOURCE_DIR}/driver_config.h") set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${CLEAN_FILES}") add_custom_target(configure_driver ALL COMMAND "${CMAKE_COMMAND}" -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/driver_config.h driver_config.h WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" VERBATIM) # 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 "${CMAKE_COMMAND}" -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/Makefile.dkms Makefile COMMAND ${MAKE_COMMAND} COMMAND "${CMAKE_COMMAND}" -E copy_if_different ${PROBE_NAME}.ko "${CMAKE_CURRENT_BINARY_DIR}" DEPENDS configure_driver WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" VERBATIM) else() add_custom_target(driver COMMAND "${CMAKE_COMMAND}" -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/Makefile.dkms Makefile COMMAND ${MAKE_COMMAND} COMMAND "${CMAKE_COMMAND}" -E copy_if_different ${PROBE_NAME}.ko "${CMAKE_CURRENT_BINARY_DIR}" DEPENDS configure_driver WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" VERBATIM) endif() add_custom_target(install_driver COMMAND ${MAKE_COMMAND} install DEPENDS driver WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" VERBATIM) if(ENABLE_DKMS) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Makefile.dkms RENAME Makefile DESTINATION "src/${PACKAGE_NAME}-${PROBE_VERSION}" COMPONENT agent) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dkms.conf dynamic_params_table.c driver_config.h event_table.c flags_table.c main.c ppm.h ppm_events.c ppm_events.h ppm_events_public.h ppm_fillers.c ppm_ringbuffer.h ppm_syscall.h syscall_table.c ppm_cputime.c ppm_compat_unistd_32.h DESTINATION "src/${PACKAGE_NAME}-${PROBE_VERSION}" COMPONENT agent) endif() sysdig-0.19.1/driver/Makefile.in000066400000000000000000000006421316537151600164560ustar00rootroot00000000000000@PROBE_NAME@-y += main.o dynamic_params_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.19.1/driver/dkms.conf.in000066400000000000000000000002361316537151600166220ustar00rootroot00000000000000PACKAGE_NAME="@PACKAGE_NAME@" PACKAGE_VERSION="@PROBE_VERSION@" BUILT_MODULE_NAME[0]="@PROBE_NAME@" DEST_MODULE_LOCATION[0]="/kernel/extra" AUTOINSTALL="yes" sysdig-0.19.1/driver/driver_config.h.in000066400000000000000000000002151316537151600200030ustar00rootroot00000000000000#pragma once #define PROBE_VERSION "${PROBE_VERSION}" #define PROBE_NAME "${PROBE_NAME}" #define PROBE_DEVICE_NAME "${PROBE_DEVICE_NAME}" sysdig-0.19.1/driver/dynamic_params_table.c000066400000000000000000000014661316537151600207200ustar00rootroot00000000000000/* 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 "ppm_events_public.h" 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}, }; sysdig-0.19.1/driver/event_table.c000066400000000000000000001311521316537151600170460ustar00rootroot00000000000000/* 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 "ppm_events_public.h" #include "ppm.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, 4, {{"fd", PT_FD, PF_DEC}, {"name", PT_FSPATH, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, file_flags}, {"mode", PT_UINT32, PF_OCT} } }, /* 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 | EF_DROP_FALCO, 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 | EF_DROP_FALCO, 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 | EF_DROP_FALCO, 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 | EF_DROP_FALCO, 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_NONE, 0}, /* PPME_SOCKET_GETSOCKNAME_X */{"getsockname", EC_NET, EF_NONE, 0}, /* PPME_SOCKET_GETPEERNAME_E */{"getpeername", EC_NET, EF_NONE, 0}, /* PPME_SOCKET_GETPEERNAME_X */{"getpeername", EC_NET, EF_NONE, 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_SOECKETPAIR_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_NONE, 0}, /* PPME_SOCKET_GETSOCKOPT_E */{"getsockopt", EC_NET, EF_NONE, 0}, /* PPME_SOCKET_GETSOCKOPT_X */{"getsockopt", EC_NET, EF_NONE, 0}, /* PPME_SOCKET_SENDMSG_E */{"sendmsg", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_MODIFIES_STATE | EF_DROP_FALCO, 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 | EF_DROP_FALCO, 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 | EF_DROP_FALCO, 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SOCKET_RECVMSG_X */{"recvmsg", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE | EF_DROP_FALCO, 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, 3, {{"fd", PT_FD, PF_DEC}, {"name", PT_FSPATH, PF_NA}, {"mode", 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_NONE, 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_NONE, 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, 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, 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_LINK_E */{"link", EC_FILE, EF_NONE, 2, {{"oldpath", PT_FSPATH, PF_NA}, {"newpath", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_LINK_X */{"link", EC_FILE, EF_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_LINKAT_E */{"linkat", EC_FILE, EF_NONE, 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_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_UNLINK_E */{"unlink", EC_FILE, EF_NONE, 1, {{"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_UNLINK_X */{"unlink", EC_FILE, EF_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_UNLINKAT_E */{"unlinkat", EC_FILE, EF_NONE, 2, {{"dirfd", PT_FD, PF_DEC}, {"name", PT_CHARBUF, PF_NA} } }, /* PPME_SYSCALL_UNLINKAT_X */{"unlinkat", EC_FILE, EF_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_PREAD_E */{"pread", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD, 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, 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, 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, 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_NONE, 1, {{"resource", PT_FLAGS8, PF_DEC, rlimit_resources} } }, /* PPME_SYSCALL_GETRLIMIT_X */{"getrlimit", EC_PROCESS, EF_NONE, 3, {{"res", PT_ERRNO, PF_DEC}, {"cur", PT_INT64, PF_DEC}, {"max", PT_INT64, PF_DEC} } }, /* PPME_SYSCALL_SETRLIMIT_E */{"setrlimit", EC_PROCESS, EF_NONE, 1, {{"resource", PT_FLAGS8, PF_DEC, rlimit_resources} } }, /* PPME_SYSCALL_SETRLIMIT_X */{"setrlimit", EC_PROCESS, EF_NONE, 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_NONE, 1, {{"addr", PT_UINT64, PF_HEX} } }, /* PPME_SYSCALL_BRK_4_X */{"brk", EC_MEMORY, EF_NONE, 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, 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, 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_NONE, 0}, /* PPME_SYSCALL_GETUID_X */ {"getuid", EC_USER, EF_NONE, 1, {{"uid", PT_UID, PF_DEC} } }, /* PPME_SYSCALL_GETEUID_E */ {"geteuid", EC_USER, EF_NONE, 0 }, /* PPME_SYSCALL_GETEUID_X */ {"geteuid", EC_USER, EF_NONE, 1, {{"euid", PT_UID, PF_DEC} } }, /* PPME_SYSCALL_GETGID_E */ {"getgid", EC_USER, EF_NONE, 0}, /* PPME_SYSCALL_GETGID_X */ {"getgid", EC_USER, EF_NONE, 1, {{"gid", PT_GID, PF_DEC} } }, /* PPME_SYSCALL_GETEGID_E */ {"getegid", EC_USER, EF_NONE, 0 }, /* PPME_SYSCALL_GETEGID_X */ {"getegid", EC_USER, EF_NONE, 1, {{"egid", PT_GID, PF_DEC} } }, /* PPME_SYSCALL_GETRESUID_E */ {"getresuid", EC_USER, EF_NONE, 0 }, /* PPME_SYSCALL_GETRESUID_X */ {"getresuid", EC_USER, EF_NONE, 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_NONE, 0 }, /* PPME_SYSCALL_GETRESGID_X */ {"getresgid", EC_USER, EF_NONE, 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_NONE, 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, 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_NONE, 1, {{"semid", PT_INT32, PF_DEC} } }, /* PPME_SYSCALL_SEMOP_X */ {"semop", EC_PROCESS, EF_NONE, 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_NONE, 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_NONE, 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_NONE, 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_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_ACCESS_E */{"access", EC_FILE, EF_NONE, 1, {{"mode", PT_FLAGS32, PF_HEX, access_flags} } }, /* PPME_SYSCALL_ACCESS_X */{"access", EC_FILE, EF_NONE, 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_INTERNAL, EF_SKIPPARSERESET | EF_MODIFIES_STATE, 1, {{"json", PT_CHARBUF, PF_NA} } }, /* PPME_CONTAINER_JSON_X */{"container", EC_INTERNAL, 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, 1, {{"filename", PT_FSPATH, PF_NA}} }, /* PPME_SYSCALL_EXECVE_18_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 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} }; sysdig-0.19.1/driver/flags_table.c000066400000000000000000000317171316537151600170270ustar00rootroot00000000000000/* 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 "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}, { }, }; 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}, { }, }; 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}, { }, }; 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}, { }, }; 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}, { }, }; 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}, { }, }; /* 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}, { }, }; /* 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}, { }, }; const struct ppm_name_value lseek_whence[] = { {"SEEK_END", PPM_SEEK_END}, {"SEEK_CUR", PPM_SEEK_CUR}, {"SEEK_SET", PPM_SEEK_SET}, { }, }; const struct ppm_name_value shutdown_how[] = { {"SHUT_RDWR", PPM_SHUT_RDWR}, {"SHUT_WR", PPM_SHUT_WR}, {"SHUT_RD", PPM_SHUT_RD}, { }, }; 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}, { }, }; 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}, { }, }; 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}, { }, }; 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}, { }, }; 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}, { }, }; 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}, { }, }; const struct ppm_name_value quotactl_dqi_flags[] = { {"DQF_NONE", PPM_DQF_NONE}, {"V1_DQF_RSQUASH", PPM_V1_DQF_RSQUASH}, { } }; 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}, { }, }; const struct ppm_name_value quotactl_types[] = { {"USRQUOTA", PPM_USRQUOTA}, {"GRPQUOTA", PPM_GRPQUOTA}, { }, }; 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}, { } }; const struct ppm_name_value semop_flags[] = { {"IPC_NOWAIT", PPM_IPC_NOWAIT}, {"SEM_UNDO", PPM_SEM_UNDO}, { }, }; const struct ppm_name_value semget_flags[] = { {"IPC_EXCL", PPM_IPC_EXCL}, {"IPC_CREAT", PPM_IPC_CREAT}, { }, }; 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}, { }, }; 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}, { }, }; 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}, { }, }; sysdig-0.19.1/driver/main.c000066400000000000000000002040551316537151600155050ustar00rootroot00000000000000/* 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 . */ #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 /* For NR_syscalls */ #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 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 static struct tracepoint *tp_page_fault_user; static struct tracepoint *tp_page_fault_kernel; static bool g_fault_tracepoint_registered; #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) /* 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; 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; } 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_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 > NR_syscalls) { 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_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, 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, 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, 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[2]; unsigned long __user *scargs; int socketcall_id; syscall_get_arguments(current, regs, 0, 2, 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; } } 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) { unsigned long close_arg = 0; int close_fd = -1; struct files_struct *files; struct fdtable *fdt; bool close_return = false; /* * 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 */ if (consumer->dropping_mode) { if (event_type == PPME_SYSCALL_CLOSE_X) { if (syscall_get_return_value(current, regs) < 0) close_return = true; } else if (event_type == PPME_SYSCALL_CLOSE_E) { syscall_get_arguments(current, regs, 0, 1, &close_arg); close_fd = (int)close_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 ) { close_return = true; } spin_unlock(&files->file_lock); } if (close_return) return 1; } 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++; } } else if (event_datap->category == PPMC_SIGNAL) { if (event_type == PPME_SIGNALDELIVER_E) ASSERT(event_datap->event_info.signal_data.info != NULL); } /* * 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 = true; 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; /* * 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 (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 (g_ppm_events[event_type].filler_callback == PPM_AUTOFILL) { /* * This event is automatically filled. Hand it to f_sys_autofill. */ cbres = f_sys_autofill(&args, &g_ppm_events[event_type]); } else { /* * There's a callback function for this event */ cbres = g_ppm_events[event_type].filler_callback(&args); } 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 insterted 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 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; 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) { /* 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. */ struct event_data_t event_data; 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_err("failed to find page_fault_user tracepoint\n"); return -ENOENT; } if (!tp_page_fault_kernel) { pr_err("failed to find page_fault_kernel tracepoint\n"); return -ENOENT; } #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 initalizations. */ g_tracepoint_registered = false; return 0; init_module_err: for (j = 0; j < n_created_devices; ++j) { device_destroy(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) { device_destroy(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.19.1/driver/ppm.h000066400000000000000000000071041316537151600153560ustar00rootroot00000000000000/* 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 /* * 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 80 #define RW_SNAPLEN_EVENT 4096 #define RW_MAX_SNAPLEN (256 * 1024 * 1024) #define DPI_LOOKAHED_SIZE 16 #define PPM_NULL_RDEV MKDEV(1, 3) #define PPM_PORT_MYSQL 3306 #define PPM_PORT_POSTGRES 5432 #define PPM_PORT_STATSD 8125 /* * Global enums */ 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), }; /* * Global structs */ struct syscall_evt_pair { int flags; enum ppm_event_type enter_event_type; enum ppm_event_type exit_event_type; }; /* * 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; }; #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 #define SYSCALL_TABLE_SIZE 512 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 sysdig-0.19.1/driver/ppm_compat_unistd_32.h000066400000000000000000000316061316537151600206170ustar00rootroot00000000000000#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.19.1/driver/ppm_cputime.c000066400000000000000000000212441316537151600171000ustar00rootroot00000000000000#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" #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 static unsigned long long vtime_delta(struct task_struct *tsk) { unsigned long long clock; clock = local_clock(); if (clock < tsk->vtime_snap) return 0; return clock - tsk->vtime_snap; } 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(&t->vtime_seqlock); if (u_dst) *u_dst = *u_src; if (s_dst) *s_dst = *s_src; /* Task is sleeping, nothing to add */ if (t->vtime_snap_whence == 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 (t->vtime_snap_whence == VTIME_USER || t->flags & PF_VCPU) { *udelta = delta; } else { if (t->vtime_snap_whence == VTIME_SYS) *sdelta = delta; } } while (read_seqretry(&t->vtime_seqlock, 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.19.1/driver/ppm_events.c000066400000000000000000001122161316537151600167360ustar00rootroot00000000000000/* 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 . */ #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 #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" /* * 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 #define ppm_access_ok access_ok #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]); } /* * 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 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; int sock_address_len; int peer_address_len; 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->ops->getname(sock, (struct sockaddr *)&sock_address, &sock_address_len, 0); if (err == 0) { if(args->event_type == PPME_SOCKET_SENDTO_X) { unsigned long val; struct sockaddr __user * usrsockaddr; /* * Get the address */ if (!args->is_socketcall) syscall_get_arguments(current, args->regs, 4, 1, &val); 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->ops->getname(sock, (struct sockaddr *)&peer_address, &peer_address_len, 1); } else { /* * Get the address len */ if (!args->is_socketcall) syscall_get_arguments(current, args->regs, 5, 1, &val); else val = args->socketcall_args[5]; if (val != 0) { peer_address_len = val; /* * 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->ops->getname(sock, (struct sockaddr *)&peer_address, &peer_address_len, 1); } } } else if (args->event_type == PPME_SOCKET_SENDMSG_X) { 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) syscall_get_arguments(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)))) { 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) { peer_address_len = addrlen; /* * Copy the address */ err = addr_to_kernel(usrsockaddr, peer_address_len, (struct sockaddr *)&peer_address); } else /* * Suppose it is a connected socket, fall back to fd */ err = sock->ops->getname(sock, (struct sockaddr *)&peer_address, &peer_address_len, 1); } else err = sock->ops->getname(sock, (struct sockaddr *)&peer_address, &peer_address_len, 1); if (err == 0) { 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 (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 commmand */ (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 == PPM_PORT_STATSD) { 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, u16 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)); 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; 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(args->arg_data_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; *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, args->arg_data_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, args->arg_data_size); if (++len > args->arg_data_size) len = args->arg_data_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)", args->arg_data_size); if (++len > args->arg_data_size) len = args->arg_data_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_LOOKAHED_SIZE; if (dpi_lookahead_size > val_len) dpi_lookahead_size = val_len; if (unlikely(dpi_lookahead_size >= args->arg_data_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) >= args->arg_data_size)) val_len = args->arg_data_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 >= args->arg_data_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 >= args->arg_data_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(args->arg_data_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(args->arg_data_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_UID: case PT_GID: case PT_SIGSET: if (likely(args->arg_data_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(args->arg_data_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(args->arg_data_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(args->arg_data_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(args->arg_data_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(args->arg_data_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 <= 65535); ASSERT(len <= args->arg_data_size); *psize += (u16)len; args->curarg++; args->arg_data_offset += len; args->arg_data_size -= len; return PPM_SUCCESS; } static 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 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; int sock_address_len; int peer_address_len; /* * 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->ops->getname(sock, (struct sockaddr *)&sock_address, &sock_address_len, 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->ops->getname(sock, (struct sockaddr *)&peer_address, &peer_address_len, 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->ops->getname(sock, (struct sockaddr *)&peer_address, &peer_address_len, 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->ops->getname(sock, (struct sockaddr *)&peer_address, &peer_address_len, 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; u32 copylen; u32 j; u64 size = 0; unsigned long bufsize; char *targetbuf = args->str_storage; u32 targetbuflen = STR_STORAGE_SIZE; unsigned long val; u32 notcopied_len; size_t tocopy_len; copylen = iovcnt * sizeof(struct iovec); 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) syscall_get_arguments(current, args->regs, 0, 1, &val); 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; u32 copylen; u32 j; u64 size = 0; unsigned long bufsize; char *targetbuf = args->str_storage; u32 targetbuflen = STR_STORAGE_SIZE; unsigned long val; u32 notcopied_len; compat_size_t tocopy_len; copylen = iovcnt * sizeof(struct compat_iovec); 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) syscall_get_arguments(current, args->regs, 0, 1, &val); 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, const struct ppm_event_entry *evinfo) { int res; unsigned long val; u32 j; int64_t retval; 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 */ syscall_get_arguments(current, args->regs, evinfo->autofill_args[j].id, 1, &val); } 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.19.1/driver/ppm_events.h000066400000000000000000000116341316537151600167450ustar00rootroot00000000000000/* 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 . */ #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 /* * 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 schduled out */ struct task_struct *sched_next; /* for context switch events, the task that is being schduled 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 */ }; /* * Filler table-related definitions */ #define PPM_AUTOFILL NULL #define PPM_MAX_AUTOFILL_ARGS 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 typedef int (*filler_callback) (struct event_filler_arguments *args); struct ppm_autofill_arg { #define AF_ID_RETVAL -1 #define AF_ID_USEDEFAULT -2 int16_t id; long default_val; }; enum autofill_paramtype { APT_REG, APT_SOCK, }; struct ppm_event_entry { filler_callback filler_callback; u16 n_autofill_args; enum autofill_paramtype paramtype; struct ppm_autofill_arg autofill_args[PPM_MAX_AUTOFILL_ARGS]; }; extern const struct ppm_event_entry g_ppm_events[]; /* * 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 /* * 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 f_sys_autofill(struct event_filler_arguments *args, const struct ppm_event_entry *evinfo); int32_t val_to_ring(struct event_filler_arguments *args, u64 val, u16 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.19.1/driver/ppm_events_public.h000066400000000000000000001241341316537151600203030ustar00rootroot00000000000000/* 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 . */ #ifndef EVENTS_PUBLIC_H_ #define EVENTS_PUBLIC_H_ #if defined(__sun) #include #endif #ifdef __KERNEL__ #include #endif /* * Limits */ #define PPM_MAX_EVENT_PARAMS 20 /* 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) /* * 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 /* * openat() flags */ #define PPM_AT_FDCWD -100 /* * 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 /* * 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 /* * 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, PPM_EVENT_MAX = 292 }; /*@}*/ /* * 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_MAX = 314, }; /* * 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_MAX = 38 /* 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]; /**< Paramter name, e.g. 'size'. */ enum ppm_param_type type; /**< Paramter 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. */ }; /*! \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. */ /* XXX this 16 limit comes out of my ass. Determine something that makes sense or use a dynamic array. */ struct ppm_param_info params[PPM_MAX_EVENT_PARAMS]; /**< parameters descriptions. */ }; #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 */ }; #if defined __sun #pragma pack() #else #pragma pack(pop) #endif /* * IOCTL codes */ #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) 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 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_param_info ptrace_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]; }; #endif /* EVENTS_PUBLIC_H_ */ sysdig-0.19.1/driver/ppm_fillers.c000066400000000000000000004021401316537151600170700ustar00rootroot00000000000000/* 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 . */ #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 #ifdef CONFIG_CGROUPS #include #endif #include #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" /* 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 CONFIG_64BIT #define _64BIT_ARGS_SINGLE_REGISTER #endif static int f_sys_generic(struct event_filler_arguments *args); /* generic syscall event filler that includes the system call number */ static int f_sys_empty(struct event_filler_arguments *args); /* empty filler */ static int f_sys_single(struct event_filler_arguments *args); /* generic enter filler that copies a single argument syscall into a single parameter event */ static int f_sys_single_x(struct event_filler_arguments *args); /* generic exit filler that captures an integer */ static int f_sys_open_x(struct event_filler_arguments *args); static int f_sys_read_x(struct event_filler_arguments *args); static int f_sys_write_x(struct event_filler_arguments *args); static int f_sys_execve_e(struct event_filler_arguments *args); static int f_proc_startupdate(struct event_filler_arguments *args); static int f_sys_socketpair_x(struct event_filler_arguments *args); static int f_sys_connect_x(struct event_filler_arguments *args); static int f_sys_accept4_e(struct event_filler_arguments *args); static int f_sys_accept_x(struct event_filler_arguments *args); static int f_sys_send_e(struct event_filler_arguments *args); static int f_sys_send_x(struct event_filler_arguments *args); static int f_sys_sendto_e(struct event_filler_arguments *args); static int f_sys_sendmsg_e(struct event_filler_arguments *args); static int f_sys_sendmsg_x(struct event_filler_arguments *args); static int f_sys_recv_e(struct event_filler_arguments *args); static int f_sys_recv_x(struct event_filler_arguments *args); static int f_sys_recvfrom_e(struct event_filler_arguments *args); static int f_sys_recvfrom_x(struct event_filler_arguments *args); static int f_sys_recvmsg_e(struct event_filler_arguments *args); static int f_sys_recvmsg_x(struct event_filler_arguments *args); static int f_sys_shutdown_e(struct event_filler_arguments *args); static int f_sys_pipe_x(struct event_filler_arguments *args); static int f_sys_eventfd_e(struct event_filler_arguments *args); static int f_sys_futex_e(struct event_filler_arguments *args); static int f_sys_lseek_e(struct event_filler_arguments *args); static int f_sys_llseek_e(struct event_filler_arguments *args); static int f_sys_socket_bind_x(struct event_filler_arguments *args); static int f_sys_poll_e(struct event_filler_arguments *args); static int f_sys_poll_x(struct event_filler_arguments *args); static int f_sys_openat_e(struct event_filler_arguments *args); #ifndef _64BIT_ARGS_SINGLE_REGISTER static int f_sys_pread64_e(struct event_filler_arguments *args); static int f_sys_preadv_e(struct event_filler_arguments *args); #endif static int f_sys_writev_e(struct event_filler_arguments *args); static int f_sys_pwrite64_e(struct event_filler_arguments *args); static int f_sys_readv_x(struct event_filler_arguments *args); static int f_sys_writev_e(struct event_filler_arguments *args); static int f_sys_writev_pwritev_x(struct event_filler_arguments *args); static int f_sys_preadv_x(struct event_filler_arguments *args); static int f_sys_pwritev_e(struct event_filler_arguments *args); static int f_sys_nanosleep_e(struct event_filler_arguments *args); static int f_sys_getrlimit_setrlimit_e(struct event_filler_arguments *args); static int f_sys_getrlimit_setrlrimit_x(struct event_filler_arguments *args); static int f_sys_prlimit_e(struct event_filler_arguments *args); static int f_sys_prlimit_x(struct event_filler_arguments *args); #ifdef CAPTURE_CONTEXT_SWITCHES static int f_sched_switch_e(struct event_filler_arguments *args); #endif static int f_sched_drop(struct event_filler_arguments *args); static int f_sched_fcntl_e(struct event_filler_arguments *args); static int f_sys_ptrace_e(struct event_filler_arguments *args); static int f_sys_ptrace_x(struct event_filler_arguments *args); static int f_sys_mmap_e(struct event_filler_arguments *args); static int f_sys_brk_munmap_mmap_x(struct event_filler_arguments *args); static int f_sys_renameat_x(struct event_filler_arguments *args); static int f_sys_symlinkat_x(struct event_filler_arguments *args); static int f_sys_procexit_e(struct event_filler_arguments *args); static int f_sys_sendfile_e(struct event_filler_arguments *args); static int f_sys_sendfile_x(struct event_filler_arguments *args); static int f_sys_quotactl_e(struct event_filler_arguments *args); static int f_sys_quotactl_x(struct event_filler_arguments *args); static int f_sys_sysdigevent_e(struct event_filler_arguments *args); static int f_sys_getresuid_and_gid_x(struct event_filler_arguments *args); #ifdef CAPTURE_SIGNAL_DELIVERIES static int f_sys_signaldeliver_e(struct event_filler_arguments *args); #endif #ifdef CAPTURE_PAGE_FAULTS static int f_sys_pagefault_e(struct event_filler_arguments *args); #endif static int f_sys_setns_e(struct event_filler_arguments *args); static int f_sys_unshare_e(struct event_filler_arguments *args); static int f_sys_flock_e(struct event_filler_arguments *args); static int f_cpu_hotplug_e(struct event_filler_arguments *args); static int f_sys_semop_e(struct event_filler_arguments *args); static int f_sys_semop_x(struct event_filler_arguments *args); static int f_sys_semget_e(struct event_filler_arguments *args); static int f_sys_semctl_e(struct event_filler_arguments *args); static int f_sys_semctl_x(struct event_filler_arguments *args); static int f_sys_ppoll_e(struct event_filler_arguments *args); static int f_sys_mount_e(struct event_filler_arguments *args); static int f_sys_access_e(struct event_filler_arguments *args); static int f_sys_access_x(struct event_filler_arguments *args); /* * Note, this is not part of g_event_info because we want to share g_event_info with userland. * However, separating this information in a different struct is not ideal and we should find a better way. */ const struct ppm_event_entry g_ppm_events[PPM_EVENT_MAX] = { [PPME_GENERIC_E] = {f_sys_generic}, [PPME_GENERIC_X] = {f_sys_generic}, [PPME_SYSCALL_OPEN_E] = {f_sys_empty}, [PPME_SYSCALL_OPEN_X] = {f_sys_open_x}, [PPME_SYSCALL_CREAT_E] = {f_sys_empty}, [PPME_SYSCALL_CREAT_X] = {PPM_AUTOFILL, 3, APT_REG, {{AF_ID_RETVAL}, {0}, {AF_ID_USEDEFAULT, 0} } }, [PPME_SYSCALL_CLOSE_E] = {f_sys_single}, [PPME_SYSCALL_CLOSE_X] = {f_sys_single_x}, [PPME_SYSCALL_READ_E] = {PPM_AUTOFILL, 2, APT_REG, {{0}, {2} } }, [PPME_SYSCALL_READ_X] = {f_sys_read_x}, [PPME_SYSCALL_WRITE_E] = {PPM_AUTOFILL, 2, APT_REG, {{0}, {2} } }, [PPME_SYSCALL_WRITE_X] = {f_sys_write_x}, [PPME_PROCEXIT_1_E] = {f_sys_procexit_e}, [PPME_SOCKET_SOCKET_E] = {PPM_AUTOFILL, 3, APT_SOCK, {{0}, {1}, {2} } }, [PPME_SOCKET_SOCKET_X] = {f_sys_single_x}, [PPME_SOCKET_SOCKETPAIR_E] = {PPM_AUTOFILL, 3, APT_SOCK, {{0}, {1}, {2} } }, [PPME_SOCKET_SOCKETPAIR_X] = {f_sys_socketpair_x}, [PPME_SOCKET_BIND_E] = {PPM_AUTOFILL, 1, APT_SOCK, {{0} } }, [PPME_SOCKET_BIND_X] = {f_sys_socket_bind_x}, [PPME_SOCKET_CONNECT_E] = {PPM_AUTOFILL, 1, APT_SOCK, {{0} } }, [PPME_SOCKET_CONNECT_X] = {f_sys_connect_x}, [PPME_SOCKET_LISTEN_E] = {PPM_AUTOFILL, 2, APT_SOCK, {{0}, {1} } }, [PPME_SOCKET_LISTEN_X] = {f_sys_single_x}, [PPME_SOCKET_ACCEPT_5_E] = {f_sys_empty}, [PPME_SOCKET_ACCEPT_5_X] = {f_sys_accept_x}, [PPME_SOCKET_ACCEPT4_5_E] = {f_sys_accept4_e}, [PPME_SOCKET_ACCEPT4_5_X] = {f_sys_accept_x}, [PPME_SOCKET_SEND_E] = {f_sys_send_e}, [PPME_SOCKET_SEND_X] = {f_sys_send_x}, [PPME_SOCKET_SENDTO_E] = {f_sys_sendto_e}, [PPME_SOCKET_SENDTO_X] = {f_sys_send_x}, [PPME_SOCKET_SENDMSG_E] = {f_sys_sendmsg_e}, [PPME_SOCKET_SENDMSG_X] = {f_sys_sendmsg_x}, [PPME_SOCKET_RECV_E] = {f_sys_recv_e}, [PPME_SOCKET_RECV_X] = {f_sys_recv_x}, [PPME_SOCKET_RECVFROM_E] = {f_sys_recvfrom_e}, [PPME_SOCKET_RECVFROM_X] = {f_sys_recvfrom_x}, [PPME_SOCKET_RECVMSG_E] = {f_sys_recvmsg_e}, [PPME_SOCKET_RECVMSG_X] = {f_sys_recvmsg_x}, [PPME_SOCKET_SHUTDOWN_E] = {f_sys_shutdown_e}, [PPME_SOCKET_SHUTDOWN_X] = {f_sys_single_x}, [PPME_SYSCALL_PIPE_E] = {f_sys_empty}, [PPME_SYSCALL_PIPE_X] = {f_sys_pipe_x}, [PPME_SYSCALL_EVENTFD_E] = {f_sys_eventfd_e}, [PPME_SYSCALL_EVENTFD_X] = {f_sys_single_x}, [PPME_SYSCALL_FUTEX_E] = {f_sys_futex_e}, [PPME_SYSCALL_FUTEX_X] = {f_sys_single_x}, [PPME_SYSCALL_STAT_E] = {f_sys_empty}, [PPME_SYSCALL_STAT_X] = {PPM_AUTOFILL, 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_LSTAT_E] = {f_sys_empty}, [PPME_SYSCALL_LSTAT_X] = {PPM_AUTOFILL, 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_FSTAT_E] = {f_sys_single}, [PPME_SYSCALL_FSTAT_X] = {f_sys_single_x}, [PPME_SYSCALL_STAT64_E] = {f_sys_empty}, [PPME_SYSCALL_STAT64_X] = {PPM_AUTOFILL, 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_LSTAT64_E] = {f_sys_empty}, [PPME_SYSCALL_LSTAT64_X] = {PPM_AUTOFILL, 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_FSTAT64_E] = {f_sys_single}, [PPME_SYSCALL_FSTAT64_X] = {f_sys_single_x}, [PPME_SYSCALL_EPOLLWAIT_E] = {PPM_AUTOFILL, 1, APT_REG, {{2} } }, [PPME_SYSCALL_EPOLLWAIT_X] = {f_sys_single_x}, [PPME_SYSCALL_POLL_E] = {f_sys_poll_e}, [PPME_SYSCALL_POLL_X] = {f_sys_poll_x}, [PPME_SYSCALL_SELECT_E] = {f_sys_empty}, [PPME_SYSCALL_SELECT_X] = {f_sys_single_x}, [PPME_SYSCALL_NEWSELECT_E] = {f_sys_empty}, [PPME_SYSCALL_NEWSELECT_X] = {f_sys_single_x}, [PPME_SYSCALL_LSEEK_E] = {f_sys_lseek_e}, [PPME_SYSCALL_LSEEK_X] = {f_sys_single_x}, [PPME_SYSCALL_LLSEEK_E] = {f_sys_llseek_e}, [PPME_SYSCALL_LLSEEK_X] = {f_sys_single_x}, [PPME_SYSCALL_IOCTL_3_E] = {PPM_AUTOFILL, 3, APT_REG, {{0}, {1}, {2} } }, [PPME_SYSCALL_IOCTL_3_X] = {f_sys_single_x}, [PPME_SYSCALL_GETCWD_E] = {f_sys_empty}, [PPME_SYSCALL_GETCWD_X] = {PPM_AUTOFILL, 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_CHDIR_E] = {f_sys_empty}, [PPME_SYSCALL_CHDIR_X] = {PPM_AUTOFILL, 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_FCHDIR_E] = {f_sys_single}, [PPME_SYSCALL_FCHDIR_X] = {f_sys_single_x}, [PPME_SYSCALL_MKDIR_E] = {PPM_AUTOFILL, 2, APT_REG, {{0}, {AF_ID_USEDEFAULT, 0} } }, [PPME_SYSCALL_MKDIR_X] = {f_sys_single_x}, [PPME_SYSCALL_RMDIR_E] = {f_sys_single}, [PPME_SYSCALL_RMDIR_X] = {f_sys_single_x}, [PPME_SYSCALL_OPENAT_E] = {f_sys_openat_e}, [PPME_SYSCALL_OPENAT_X] = {f_sys_single_x}, [PPME_SYSCALL_LINK_E] = {PPM_AUTOFILL, 2, APT_REG, {{0}, {1} } }, [PPME_SYSCALL_LINK_X] = {f_sys_single_x}, [PPME_SYSCALL_LINKAT_E] = {PPM_AUTOFILL, 4, APT_REG, {{0}, {1}, {2}, {3} } }, [PPME_SYSCALL_LINKAT_X] = {f_sys_single_x}, [PPME_SYSCALL_UNLINK_E] = {f_sys_single}, [PPME_SYSCALL_UNLINK_X] = {f_sys_single_x}, [PPME_SYSCALL_UNLINKAT_E] = {PPM_AUTOFILL, 2, APT_REG, {{0}, {1} } }, [PPME_SYSCALL_UNLINKAT_X] = {f_sys_single_x}, #ifdef _64BIT_ARGS_SINGLE_REGISTER [PPME_SYSCALL_PREAD_E] = {PPM_AUTOFILL, 3, APT_REG, {{0}, {2}, {3} } }, #else [PPME_SYSCALL_PREAD_E] = {f_sys_pread64_e}, #endif [PPME_SYSCALL_PREAD_X] = {f_sys_read_x}, [PPME_SYSCALL_PWRITE_E] = {f_sys_pwrite64_e}, [PPME_SYSCALL_PWRITE_X] = {f_sys_write_x}, [PPME_SYSCALL_READV_E] = {f_sys_single}, [PPME_SYSCALL_READV_X] = {f_sys_readv_x}, [PPME_SYSCALL_WRITEV_E] = {f_sys_writev_e}, [PPME_SYSCALL_WRITEV_X] = {f_sys_writev_pwritev_x}, #ifdef _64BIT_ARGS_SINGLE_REGISTER [PPME_SYSCALL_PREADV_E] = {PPM_AUTOFILL, 2, APT_REG, {{0}, {3} } }, #else [PPME_SYSCALL_PREADV_E] = {f_sys_preadv_e}, #endif [PPME_SYSCALL_PREADV_X] = {f_sys_preadv_x}, [PPME_SYSCALL_PWRITEV_E] = {f_sys_pwritev_e}, [PPME_SYSCALL_PWRITEV_X] = {f_sys_writev_pwritev_x}, [PPME_SYSCALL_DUP_E] = {PPM_AUTOFILL, 1, APT_REG, {{0} } }, [PPME_SYSCALL_DUP_X] = {f_sys_single_x}, /* Mask and Flags not implemented yet */ [PPME_SYSCALL_SIGNALFD_E] = {PPM_AUTOFILL, 3, APT_REG, {{0}, {AF_ID_USEDEFAULT, 0}, {AF_ID_USEDEFAULT, 0} } }, [PPME_SYSCALL_SIGNALFD_X] = {f_sys_single_x}, [PPME_SYSCALL_KILL_E] = {PPM_AUTOFILL, 2, APT_REG, {{0}, {1} } }, [PPME_SYSCALL_KILL_X] = {f_sys_single_x}, [PPME_SYSCALL_TKILL_E] = {PPM_AUTOFILL, 2, APT_REG, {{0}, {1} } }, [PPME_SYSCALL_TKILL_X] = {f_sys_single_x}, [PPME_SYSCALL_TGKILL_E] = {PPM_AUTOFILL, 3, APT_REG, {{0}, {1}, {2} } }, [PPME_SYSCALL_TGKILL_X] = {f_sys_single_x}, [PPME_SYSCALL_NANOSLEEP_E] = {f_sys_nanosleep_e}, [PPME_SYSCALL_NANOSLEEP_X] = {f_sys_single_x}, [PPME_SYSCALL_TIMERFD_CREATE_E] = {PPM_AUTOFILL, 2, APT_REG, {{AF_ID_USEDEFAULT, 0}, {AF_ID_USEDEFAULT, 0} } }, [PPME_SYSCALL_TIMERFD_CREATE_X] = {f_sys_single_x}, [PPME_SYSCALL_INOTIFY_INIT_E] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_USEDEFAULT, 0} } }, [PPME_SYSCALL_INOTIFY_INIT_X] = {f_sys_single_x}, [PPME_SYSCALL_GETRLIMIT_E] = {f_sys_getrlimit_setrlimit_e}, [PPME_SYSCALL_GETRLIMIT_X] = {f_sys_getrlimit_setrlrimit_x}, [PPME_SYSCALL_SETRLIMIT_E] = {f_sys_getrlimit_setrlimit_e}, [PPME_SYSCALL_SETRLIMIT_X] = {f_sys_getrlimit_setrlrimit_x}, [PPME_SYSCALL_PRLIMIT_E] = {f_sys_prlimit_e}, [PPME_SYSCALL_PRLIMIT_X] = {f_sys_prlimit_x}, #ifdef CAPTURE_CONTEXT_SWITCHES [PPME_SCHEDSWITCH_6_E] = {f_sched_switch_e}, #endif [PPME_DROP_E] = {f_sched_drop}, [PPME_DROP_X] = {f_sched_drop}, [PPME_SYSCALL_FCNTL_E] = {f_sched_fcntl_e}, [PPME_SYSCALL_FCNTL_X] = {f_sys_single_x}, [PPME_SYSCALL_EXECVE_18_E] = {f_sys_execve_e}, [PPME_SYSCALL_EXECVE_18_X] = {f_proc_startupdate}, [PPME_SYSCALL_CLONE_20_E] = {f_sys_empty}, [PPME_SYSCALL_CLONE_20_X] = {f_proc_startupdate}, [PPME_SYSCALL_BRK_4_E] = {PPM_AUTOFILL, 1, APT_REG, {{0} } }, [PPME_SYSCALL_BRK_4_X] = {f_sys_brk_munmap_mmap_x}, [PPME_SYSCALL_MMAP_E] = {f_sys_mmap_e}, [PPME_SYSCALL_MMAP_X] = {f_sys_brk_munmap_mmap_x}, [PPME_SYSCALL_MMAP2_E] = {f_sys_mmap_e}, [PPME_SYSCALL_MMAP2_X] = {f_sys_brk_munmap_mmap_x}, [PPME_SYSCALL_MUNMAP_E] = {PPM_AUTOFILL, 2, APT_REG, {{0}, {1} } }, [PPME_SYSCALL_MUNMAP_X] = {f_sys_brk_munmap_mmap_x}, [PPME_SYSCALL_SPLICE_E] = {PPM_AUTOFILL, 4, APT_REG, {{0}, {2}, {4}, {5} } }, [PPME_SYSCALL_SPLICE_X] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_PTRACE_E] = {f_sys_ptrace_e}, [PPME_SYSCALL_PTRACE_X] = {f_sys_ptrace_x}, [PPME_SYSCALL_RENAME_E] = {f_sys_empty}, [PPME_SYSCALL_RENAME_X] = {PPM_AUTOFILL, 3, APT_REG, {{AF_ID_RETVAL}, {0}, {1} } }, [PPME_SYSCALL_RENAMEAT_E] = {f_sys_empty}, [PPME_SYSCALL_RENAMEAT_X] = {f_sys_renameat_x}, [PPME_SYSCALL_SYMLINK_E] = {f_sys_empty}, [PPME_SYSCALL_SYMLINK_X] = {PPM_AUTOFILL, 3, APT_REG, {{AF_ID_RETVAL}, {0}, {1} } }, [PPME_SYSCALL_SYMLINKAT_E] = {f_sys_empty}, [PPME_SYSCALL_SYMLINKAT_X] = {f_sys_symlinkat_x}, [PPME_SYSCALL_FORK_20_E] = {f_sys_empty}, [PPME_SYSCALL_FORK_20_X] = {f_proc_startupdate}, [PPME_SYSCALL_VFORK_20_E] = {f_sys_empty}, [PPME_SYSCALL_VFORK_20_X] = {f_proc_startupdate}, [PPME_SYSCALL_SENDFILE_E] = {f_sys_sendfile_e}, [PPME_SYSCALL_SENDFILE_X] = {f_sys_sendfile_x}, [PPME_SYSCALL_QUOTACTL_E] = {f_sys_quotactl_e}, [PPME_SYSCALL_QUOTACTL_X] = {f_sys_quotactl_x}, [PPME_SYSCALL_SETRESUID_E] = {PPM_AUTOFILL, 3, APT_REG, {{0}, {1}, {2} } }, [PPME_SYSCALL_SETRESUID_X] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_SETRESGID_E] = {PPM_AUTOFILL, 3, APT_REG, {{0}, {1}, {2} } }, [PPME_SYSCALL_SETRESGID_X] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSDIGEVENT_E] = {f_sys_sysdigevent_e}, [PPME_SYSCALL_SETUID_E] = {PPM_AUTOFILL, 1, APT_REG, {{0} } }, [PPME_SYSCALL_SETUID_X] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_SETGID_E] = {PPM_AUTOFILL, 1, APT_REG, {{0} } }, [PPME_SYSCALL_SETGID_X] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_GETUID_E] = {f_sys_empty}, [PPME_SYSCALL_GETUID_X] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_GETEUID_E] = {f_sys_empty}, [PPME_SYSCALL_GETEUID_X] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_GETGID_E] = {f_sys_empty}, [PPME_SYSCALL_GETGID_X] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_GETEGID_E] = {f_sys_empty}, [PPME_SYSCALL_GETEGID_X] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_GETRESUID_E] = {f_sys_empty}, [PPME_SYSCALL_GETRESUID_X] = {f_sys_getresuid_and_gid_x}, [PPME_SYSCALL_GETRESGID_E] = {f_sys_empty}, [PPME_SYSCALL_GETRESGID_X] = {f_sys_getresuid_and_gid_x}, #ifdef CAPTURE_SIGNAL_DELIVERIES [PPME_SIGNALDELIVER_E] = {f_sys_signaldeliver_e}, [PPME_SIGNALDELIVER_X] = {f_sys_empty}, #endif [PPME_SYSCALL_GETDENTS_E] = {f_sys_single}, [PPME_SYSCALL_GETDENTS_X] = {f_sys_single_x}, [PPME_SYSCALL_GETDENTS64_E] = {f_sys_single}, [PPME_SYSCALL_GETDENTS64_X] = {f_sys_single_x}, [PPME_SYSCALL_SETNS_E] = {f_sys_setns_e}, [PPME_SYSCALL_SETNS_X] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_FLOCK_E] = {f_sys_flock_e}, [PPME_SYSCALL_FLOCK_X] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_CPU_HOTPLUG_E] = {f_cpu_hotplug_e}, [PPME_SYSCALL_SEMOP_E] = {f_sys_semop_e}, [PPME_SYSCALL_SEMOP_X] = {f_sys_semop_x}, [PPME_SYSCALL_SEMGET_E] = {f_sys_semget_e}, [PPME_SYSCALL_SEMGET_X] = {f_sys_single_x}, [PPME_SYSCALL_SEMCTL_E] = {f_sys_semctl_e}, [PPME_SYSCALL_SEMCTL_X] = {f_sys_semctl_x}, [PPME_SYSCALL_PPOLL_E] = {f_sys_ppoll_e}, [PPME_SYSCALL_PPOLL_X] = {f_sys_poll_x}, /* exit same for poll() and ppoll() */ [PPME_SYSCALL_MOUNT_E] = {f_sys_mount_e}, [PPME_SYSCALL_MOUNT_X] = {PPM_AUTOFILL, 4, APT_REG, {{AF_ID_RETVAL}, {0}, {1}, {2} } }, [PPME_SYSCALL_UMOUNT_E] = {PPM_AUTOFILL, 1, APT_REG, {{1} } }, [PPME_SYSCALL_UMOUNT_X] = {PPM_AUTOFILL, 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_ACCESS_E] = {f_sys_access_e}, [PPME_SYSCALL_ACCESS_X] = {f_sys_access_x}, [PPME_SYSCALL_CHROOT_E] = {f_sys_empty}, [PPME_SYSCALL_CHROOT_X] = {PPM_AUTOFILL, 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_SETSID_E] = {f_sys_empty}, [PPME_SYSCALL_SETSID_X] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_RETVAL} } }, [PPME_SYSCALL_MKDIR_2_E] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_USEDEFAULT, 0} } }, [PPME_SYSCALL_MKDIR_2_X] = {PPM_AUTOFILL, 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_RMDIR_2_E] = {f_sys_empty}, [PPME_SYSCALL_RMDIR_2_X] = {PPM_AUTOFILL, 2, APT_REG, {{AF_ID_RETVAL}, {0} } }, [PPME_SYSCALL_UNSHARE_E] = {f_sys_unshare_e}, [PPME_SYSCALL_UNSHARE_X] = {PPM_AUTOFILL, 1, APT_REG, {{AF_ID_RETVAL} } }, #ifdef CAPTURE_PAGE_FAULTS [PPME_PAGE_FAULT_E] = {f_sys_pagefault_e}, [PPME_PAGE_FAULT_X] = {f_sys_empty}, #endif }; #define merge_64(hi, lo) ((((unsigned long long)(hi)) << 32) + ((lo) & 0xffffffffUL)) static 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); } static int f_sys_empty(struct event_filler_arguments *args) { return add_sentinel(args); } static int f_sys_single(struct event_filler_arguments *args) { int res; unsigned long val; syscall_get_arguments(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); } static 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 u32 open_flags_to_scap(unsigned long flags) { u32 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; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) 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; if (flags & O_DIRECT) res |= PPM_O_DIRECT; if (flags & O_DIRECTORY) res |= PPM_O_DIRECTORY; if (flags & O_LARGEFILE) res |= PPM_O_LARGEFILE; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) if (flags & O_CLOEXEC) res |= PPM_O_CLOEXEC; #endif return res; } static inline u32 open_modes_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; } static inline int open_mode_to_ring(struct event_filler_arguments *args, unsigned long flags, unsigned int i) { #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 int res; if (flags & flags_mask) { unsigned long val; /* * Mode * Note that we convert them into the ppm portable * representation before pushing them to the ring */ syscall_get_arguments(current, args->regs, i, 1, &val); res = val_to_ring(args, open_modes_to_scap(val), 0, false, 0); } else { res = val_to_ring(args, 0, 0, false, 0); } return res; } static int f_sys_open_x(struct event_filler_arguments *args) { unsigned long val, flags; 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(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(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 */ res = open_mode_to_ring(args, flags, 2); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } static 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(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(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); } static 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(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(current, args->regs, 2, 1, &val); bufsize = val; /* * Copy the buffer */ syscall_get_arguments(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); } static 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; } /* * 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) { printk(pr_fmt("Error on copy here3")); 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; } static 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_18_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 suceeded. 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(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 /* * flags */ if (args->event_type == PPME_SYSCALL_CLONE_20_X) syscall_get_arguments(current, args->regs, 0, 1, &val); else val = 0; res = val_to_ring(args, (uint64_t)clone_flags_to_scap(val), 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_18_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(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; } return add_sentinel(args); } static int f_sys_execve_e(struct event_filler_arguments *args) { int res; unsigned long val; /* * filename */ syscall_get_arguments(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); } static 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(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(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); } static 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(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(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(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); } static 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 succesful, copy the FDs */ if (likely(retval >= 0)) { /* * fds */ if (!args->is_socketcall) syscall_get_arguments(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 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); } static 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(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); } static 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(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(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; } static 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; } static 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(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(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); } static 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(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(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); } static int f_sys_recv_e_common(struct event_filler_arguments *args) { int res; unsigned long val; /* * fd */ if (!args->is_socketcall) syscall_get_arguments(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; /* * size */ if (!args->is_socketcall) syscall_get_arguments(current, args->regs, 2, 1, &val); else val = args->socketcall_args[2]; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return PPM_SUCCESS; } static int f_sys_recv_e(struct event_filler_arguments *args) { int res; res = f_sys_recv_e_common(args); if (likely(res == PPM_SUCCESS)) return add_sentinel(args); return res; } static int f_sys_recvfrom_e(struct event_filler_arguments *args) { int res; res = f_sys_recv_e_common(args); if (likely(res == PPM_SUCCESS)) return add_sentinel(args); return res; } static 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(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(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; } static 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; } static 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(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(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(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); } static 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(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(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); } static 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(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); } static int f_sys_recvmsg_e(struct event_filler_arguments *args) { int res; unsigned long val; /* * fd */ if (!args->is_socketcall) syscall_get_arguments(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; return add_sentinel(args); } static 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(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(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); } static 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(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); } static int f_sys_eventfd_e(struct event_filler_arguments *args) { int res; unsigned long val; /* * initval */ syscall_get_arguments(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(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); } static 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 int f_sys_shutdown_e(struct event_filler_arguments *args) { int res; unsigned long val; /* * fd */ if (!args->is_socketcall) syscall_get_arguments(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(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); } static 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 int f_sys_futex_e(struct event_filler_arguments *args) { int res; unsigned long val; /* * addr */ syscall_get_arguments(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(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(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 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 int f_sys_lseek_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * fd */ syscall_get_arguments(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(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(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); } static 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(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(current, args->regs, 1, 1, &oh); syscall_get_arguments(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(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); } /* XXX this is very basic for the moment, we'll need to improve it */ static 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 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(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(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); } static 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(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); } static 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(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(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() */ static 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); } #define PPM_MS_MGC_MSK 0xffff0000 #define PPM_MS_MGC_VAL 0xC0ED0000 static 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(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); } static int f_sys_openat_e(struct event_filler_arguments *args) { unsigned long val, flags; int res; /* * dirfd */ syscall_get_arguments(current, args->regs, 0, 1, &val); if (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(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(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 */ res = open_mode_to_ring(args, flags, 3); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } #ifndef _64BIT_ARGS_SINGLE_REGISTER static 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(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(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(current, args->regs, 3, 1, &pos0); syscall_get_arguments(current, args->regs, 4, 1, &pos1); #elif defined CONFIG_ARM && CONFIG_AEABI syscall_get_arguments(current, args->regs, 4, 1, &pos0); syscall_get_arguments(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 */ static 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(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(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(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(current, args->regs, 3, 1, &pos0); syscall_get_arguments(current, args->regs, 4, 1, &pos1); #elif defined CONFIG_ARM && CONFIG_AEABI syscall_get_arguments(current, args->regs, 4, 1, &pos0); syscall_get_arguments(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); } static int f_sys_readv_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(current, args->regs, 1, 1, &val); syscall_get_arguments(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); } static 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(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(current, args->regs, 2, 1, &iovcnt); /* * Copy the buffer */ syscall_get_arguments(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); } static 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(current, args->regs, 2, 1, &iovcnt); /* * Copy the buffer */ syscall_get_arguments(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 static int f_sys_preadv_e(struct event_filler_arguments *args) { unsigned long val; int res; unsigned long pos0; unsigned long pos1; uint64_t pos64; /* * fd */ syscall_get_arguments(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(current, args->regs, 3, 1, &pos0); syscall_get_arguments(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 */ static int f_sys_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(current, args->regs, 2, 1, &iovcnt); syscall_get_arguments(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, 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); } static 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(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(current, args->regs, 2, 1, &iovcnt); /* * Copy the buffer */ syscall_get_arguments(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(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(current, args->regs, 3, 1, &pos0); syscall_get_arguments(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); } static int f_sys_nanosleep_e(struct event_filler_arguments *args) { unsigned long val; int res; syscall_get_arguments(current, args->regs, 0, 1, &val); res = timespec_parse(args, val); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } static 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 int f_sys_getrlimit_setrlimit_e(struct event_filler_arguments *args) { u8 ppm_resource; unsigned long val; int res; /* * resource */ syscall_get_arguments(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); } static 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(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); } static int f_sys_prlimit_e(struct event_filler_arguments *args) { u8 ppm_resource; unsigned long val; int res; /* * pid */ syscall_get_arguments(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(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); } static 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(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(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 static 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 */ static int f_sched_drop(struct event_filler_arguments *args) { int res; /* * next */ res = val_to_ring(args, args->consumer->sampling_ratio, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } static 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 int f_sched_fcntl_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * fd */ syscall_get_arguments(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(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 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 inline int parse_ptrace_addr(struct event_filler_arguments *args, u16 request) { unsigned long val; uint64_t dst; u8 idx; syscall_get_arguments(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(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); } static int f_sys_ptrace_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * request */ syscall_get_arguments(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(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); } static 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(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); } static 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); } static 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 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 int f_sys_mmap_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * addr */ syscall_get_arguments(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(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(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(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(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(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); } static 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(current, args->regs, 0, 1, &val); if (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(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(current, args->regs, 2, 1, &val); if (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(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); } static 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(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(current, args->regs, 1, 1, &val); if (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(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); } static 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); } static int f_sys_sendfile_e(struct event_filler_arguments *args) { unsigned long val; int res; off_t offset; /* * out_fd */ syscall_get_arguments(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(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(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(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); } static 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(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); } static 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 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 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 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(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(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); } static 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(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(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(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); } static 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); } static 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(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(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(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); } static 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 int f_sys_flock_e(struct event_filler_arguments *args) { unsigned long val; int res; u32 flags; syscall_get_arguments(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(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); } static int f_sys_setns_e(struct event_filler_arguments *args) { unsigned long val; int res; u32 flags; /* * parse fd */ syscall_get_arguments(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(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); } static 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(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 static 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 static 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 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 static 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); } static 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 int f_sys_semop_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * semid */ syscall_get_arguments(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); } static 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(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(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); } static 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 int f_sys_semget_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * key */ syscall_get_arguments(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(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(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); } static 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 int f_sys_semctl_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * semid */ syscall_get_arguments(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(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(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(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); } static int f_sys_semctl_x(struct event_filler_arguments *args) { int res; int64_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; return add_sentinel(args); } static 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 int f_sys_access_e(struct event_filler_arguments *args) { unsigned long val; int res; /* * mode */ syscall_get_arguments(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); } static int f_sys_access_x(struct event_filler_arguments *args) { unsigned long val; int res; int64_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; /* * pathname */ syscall_get_arguments(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); } sysdig-0.19.1/driver/ppm_ringbuffer.h000066400000000000000000000025311316537151600175660ustar00rootroot00000000000000/* 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 . */ #ifndef PPM_H_ #define PPM_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_H_ */ sysdig-0.19.1/driver/ppm_syscall.h000066400000000000000000000113341316537151600171100ustar00rootroot00000000000000/* * 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.19.1/driver/syscall_table.c000066400000000000000000002534411316537151600174050ustar00rootroot00000000000000/* 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 #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 #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 /* * 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_18_E, PPME_SYSCALL_EXECVE_18_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 | UF_ALWAYS_DROP, PPME_SYSCALL_MKDIR_2_E, PPME_SYSCALL_MKDIR_2_X}, [__NR_rmdir - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_RMDIR_2_E, PPME_SYSCALL_RMDIR_2_X}, [__NR_openat - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_OPENAT_E, PPME_SYSCALL_OPENAT_X}, [__NR_link - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_LINK_E, PPME_SYSCALL_LINK_X}, [__NR_linkat - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_LINKAT_E, PPME_SYSCALL_LINKAT_X}, [__NR_unlink - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_UNLINK_E, PPME_SYSCALL_UNLINK_X}, [__NR_unlinkat - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_UNLINKAT_E, PPME_SYSCALL_UNLINKAT_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 | UF_ALWAYS_DROP, PPME_SYSCALL_KILL_E, PPME_SYSCALL_KILL_X}, [__NR_tkill - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_TKILL_E, PPME_SYSCALL_TKILL_X}, [__NR_tgkill - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, 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}, #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 | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_lchown - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, 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 | UF_ALWAYS_DROP, PPME_SYSCALL_MOUNT_E, PPME_SYSCALL_MOUNT_X}, [__NR_umount2 - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_UMOUNT_E, PPME_SYSCALL_UMOUNT_X}, [__NR_ptrace - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, 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_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 | UF_ALWAYS_DROP, 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 | UF_ALWAYS_DROP, PPME_SYSCALL_RENAME_E, PPME_SYSCALL_RENAME_X}, [__NR_renameat - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_RENAMEAT_E, PPME_SYSCALL_RENAMEAT_X}, [__NR_symlink - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_SYMLINK_E, PPME_SYSCALL_SYMLINK_X}, [__NR_symlinkat - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, 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 | UF_ALWAYS_DROP, 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} }; /* * 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_access - SYSCALL_TABLE_ID0] = PPM_SC_ACCESS, [__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_quotactl - SYSCALL_TABLE_ID0] = PPM_SC_QUOTACTL, [__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_getuid - SYSCALL_TABLE_ID0] = PPM_SC_GETUID, [__NR_getgid - SYSCALL_TABLE_ID0] = PPM_SC_GETGID, [__NR_geteuid - SYSCALL_TABLE_ID0] = PPM_SC_GETEUID, [__NR_getegid - SYSCALL_TABLE_ID0] = PPM_SC_GETEGID, [__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_setresuid - SYSCALL_TABLE_ID0] = PPM_SC_SETRESUID, [__NR_getresuid - SYSCALL_TABLE_ID0] = PPM_SC_GETRESUID, [__NR_setresgid - SYSCALL_TABLE_ID0] = PPM_SC_SETRESGID, [__NR_getresgid - SYSCALL_TABLE_ID0] = PPM_SC_GETRESGID, [__NR_chown - SYSCALL_TABLE_ID0] = PPM_SC_CHOWN, [__NR_setuid - SYSCALL_TABLE_ID0] = PPM_SC_SETUID, [__NR_setgid - SYSCALL_TABLE_ID0] = PPM_SC_SETGID, [__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 #ifdef __NR_setns [__NR_setns - SYSCALL_TABLE_ID0] = PPM_SC_SETNS, #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 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_18_E, PPME_SYSCALL_EXECVE_18_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 | UF_ALWAYS_DROP, PPME_SYSCALL_MKDIR_2_E, PPME_SYSCALL_MKDIR_2_X}, [__NR_ia32_rmdir - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_RMDIR_2_E, PPME_SYSCALL_RMDIR_2_X}, [__NR_ia32_openat - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_OPENAT_E, PPME_SYSCALL_OPENAT_X}, [__NR_ia32_link - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_LINK_E, PPME_SYSCALL_LINK_X}, [__NR_ia32_linkat - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_LINKAT_E, PPME_SYSCALL_LINKAT_X}, [__NR_ia32_unlink - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_UNLINK_E, PPME_SYSCALL_UNLINK_X}, [__NR_ia32_unlinkat - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_UNLINKAT_E, PPME_SYSCALL_UNLINKAT_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 | UF_ALWAYS_DROP, PPME_SYSCALL_KILL_E, PPME_SYSCALL_KILL_X}, [__NR_ia32_tkill - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_TKILL_E, PPME_SYSCALL_TKILL_X}, [__NR_ia32_tgkill - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, 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}, #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 | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_lchown - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, 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 | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_umount2 - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ia32_ptrace - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, 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 | UF_ALWAYS_DROP, 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 | UF_ALWAYS_DROP, PPME_SYSCALL_RENAME_E, PPME_SYSCALL_RENAME_X}, [__NR_ia32_renameat - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_RENAMEAT_E, PPME_SYSCALL_RENAMEAT_X}, [__NR_ia32_symlink - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_SYMLINK_E, PPME_SYSCALL_SYMLINK_X}, [__NR_ia32_symlinkat - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, 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} }; /* * 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 }; #endif /* CONFIG_IA32_EMULATION */ sysdig-0.19.1/scripts/000077500000000000000000000000001316537151600146035ustar00rootroot00000000000000sysdig-0.19.1/scripts/CMakeLists.txt000066400000000000000000000006141316537151600173440ustar00rootroot00000000000000configure_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.19.1/scripts/Dockerfile.ol6000066400000000000000000000007121316537151600172740ustar00rootroot00000000000000FROM 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.19.1/scripts/Dockerfile.ol7000066400000000000000000000006651316537151600173040ustar00rootroot00000000000000FROM oraclelinux:7 RUN yum -y install \ wget \ git \ gcc \ gcc-c++ \ autoconf \ make \ cmake \ libdtrace-ctf \ 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.19.1/scripts/boot2docker-kernel-crawler.py000077500000000000000000000024711316537151600223140ustar00rootroot00000000000000#!/usr/bin/python # 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.19.1/scripts/build-probe-binaries000077500000000000000000000374701316537151600205420ustar00rootroot00000000000000#!/bin/bash # # 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 # # For continuous integration log purposes, wget prints its own output to stderr # so it can be convenient to redirect it to a file. This can be done directly # at runtime. # PROBE_NAME=$1 PROBE_VERSION=$2 REPOSITORY_NAME=$3 KERNEL_TYPE= BASEDIR=$(pwd) ARCH=$(uname -m) URL_TIMEOUT=300 RETRY=10 if [ $# -eq 4 ]; then KERNEL_TYPE=$4 fi if [ ! -d $BASEDIR/output ]; then mkdir $BASEDIR/output fi if [ $PROBE_NAME = "sysdigcloud-probe" ]; then PROBE_REPO_NAME="agent" else PROBE_REPO_NAME=$(echo $PROBE_NAME | cut -f1 -d-) fi function update_code_for { repo=$1 if [ ! -d $repo ]; then git clone git@github.com:draios/$repo.git fi cd $repo git checkout master # The UEK builder container doesn't have git credentials # It relies on the non-UEK builds doing the pull earlier if [[ ! "$KERNEL_TYPE" =~ "UEK" ]]; then git pull fi if [ $PROBE_REPO_NAME = $repo ]; then git checkout $PROBE_VERSION else git checkout $PROBE_REPO_NAME/$PROBE_VERSION fi # 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 { 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 echo Building $PROBE_NAME-$PROBE_VERSION-$ARCH-$KERNEL_RELEASE-$HASH.ko [${FUNCNAME[1]}] update_code_for sysdig if [ $PROBE_NAME != "sysdig-probe" ]; then update_code_for falco fi if [ $PROBE_NAME = "sysdigcloud-probe" ]; then update_code_for agent fi cd $PROBE_REPO_NAME mkdir build cd build version_name=-D$(echo $PROBE_REPO_NAME | tr [a-z] [A-Z])_VERSION cmake -DCMAKE_BUILD_TYPE=Release $version_name=$PROBE_VERSION .. 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 --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 --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 past runs fail, /tmp/loop and relative device # may be hanging around if mount | grep /tmp/loop; then LOOPDEV=$(mount |grep /tmp/loop|cut -d" " -f1|egrep -o "loop[0-9]+") sudo umount /tmp/loop sudo kpartx -dv /dev/$LOOPDEV fi if [ ! -f coreos_developer_container.bin ]; then wget --timeout=${URL_TIMEOUT} --tries=${RETRY} ${VERSION_URL}coreos_developer_container.bin.bz2 bunzip2 coreos_developer_container.bin.bz2 fi 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-* . cp config-* config_orig cp config_orig config # 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 KERNEL_RELEASE=$(ls config-* | sed s/config-//) HASH_ORIG=$(md5sum config_orig | cut -d' ' -f1) HASH=$(md5sum config | cut -d' ' -f1) cd $BASEDIR export KERNELDIR=$(readlink -f /tmp/loop/lib/modules/$KERNEL_RELEASE/build) build_probe cd $COREOS_DIR # cleanup sudo umount /tmp/loop sudo kpartx -dv coreos_developer_container.bin cd $BASEDIR 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 --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 --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 --timeout=${URL_TIMEOUT} --tries=${RETRY} $URL dpkg -x $DEB ./ fi NUM_DEB=$(ls linux-*.deb -1 | wc -l) if [ $NUM_DEB -eq 3 ]; 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 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] wget --timeout=${URL_TIMEOUT} --tries=${RETRY} $URL rpm2cpio $RPM | cpio -idm fi NUM_RPM=$(ls kernel-*.rpm -1 | wc -l) 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} [Ubuntu] wget --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} [Ubuntu] wget --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} } if [ -z "$KERNEL_TYPE" ]; then # # Ubuntu build # 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 # echo Building RHEL 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 # # 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 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. 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 ../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 ../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 # # Upload modules # aws s3 sync ./output/ s3://download.draios.com/$REPOSITORY_NAME/sysdig-probe-binaries/ --acl public-read echo "Success." 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.19.1/scripts/completions/000077500000000000000000000000001316537151600171375ustar00rootroot00000000000000sysdig-0.19.1/scripts/completions/bash/000077500000000000000000000000001316537151600200545ustar00rootroot00000000000000sysdig-0.19.1/scripts/completions/bash/sysdig000066400000000000000000000107001316537151600212770ustar00rootroot00000000000000_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.19.1/scripts/completions/zsh/000077500000000000000000000000001316537151600177435ustar00rootroot00000000000000sysdig-0.19.1/scripts/completions/zsh/_sysdig000066400000000000000000000272661316537151600213440ustar00rootroot00000000000000#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.19.1/scripts/debian/000077500000000000000000000000001316537151600160255ustar00rootroot00000000000000sysdig-0.19.1/scripts/debian/postinst.in000077500000000000000000000012761316537151600202510ustar00rootroot00000000000000#!/bin/sh 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.19.1/scripts/debian/prerm.in000077500000000000000000000004211316537151600175020ustar00rootroot00000000000000#!/bin/sh 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.19.1/scripts/description.txt000066400000000000000000000006231316537151600176700ustar00rootroot00000000000000Sysdig 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.19.1/scripts/install-sysdig.in000066400000000000000000000115321316537151600201030ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2013-2014 Draios inc. # # This file is part of _COMPONENT_. # # _COMPONENT_ 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. # # _COMPONENT_ 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 _COMPONENT_. If not, see . # 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-9.noarch.rpm elif [ $VERSION -eq 7 ]; then rpm --quiet -i https://mirrors.kernel.org/fedora-epel/7/x86_64/e/epel-release-7-10.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" ]; then unsupported 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) 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.19.1/scripts/kernel-crawler.py000077500000000000000000000212431316537151600200770ustar00rootroot00000000000000#!/usr/bin/python # Author: Samuele Pilleri # Date: August 17th, 2015 import sys import urllib2 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)-[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)-[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" } ], "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/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" # } ], "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" }, { "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"] }, { "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"] }, { "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"] } ] } # # 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: 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"}) for rpm in rpms: if "exclude_patterns" in repo and any(x in rpm for x in repo["exclude_patterns"]): continue else: urls.add(source + str(urllib2.unquote(rpm))) except: continue # # Print URLs to stdout # for url in urls: print(url) sysdig-0.19.1/scripts/oracle-kernel-crawler.py000077500000000000000000000071061316537151600213440ustar00rootroot00000000000000#!/usr/bin/python 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.19.1/scripts/rpm/000077500000000000000000000000001316537151600154015ustar00rootroot00000000000000sysdig-0.19.1/scripts/rpm/postinstall000077500000000000000000000011211316537151600176760ustar00rootroot00000000000000dkms 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.19.1/scripts/rpm/preuninstall000077500000000000000000000000751316537151600200510ustar00rootroot00000000000000dkms remove -m sysdig -v %{version} --all --rpm_safe_upgrade sysdig-0.19.1/scripts/sysdig-probe-loader000077500000000000000000000077511316537151600204160ustar00rootroot00000000000000#!/bin/bash # # 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. # if [ $(id -u) != 0 ]; then echo "Installer must be run as root (or with sudo)." exit 1 fi 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 if ! hash curl > /dev/null 2>&1; then echo "This program requires curl" exit 1 fi ARCH=$(uname -m) KERNEL_RELEASE=$(uname -r) SCRIPT_NAME=$(basename "$0") if [ -z "$SYSDIG_REPOSITORY" ]; then SYSDIG_REPOSITORY="stable" fi if [ "$SCRIPT_NAME" = "sysdig-probe-loader" ]; then SYSDIG_VERSION=$(sysdig --version | cut -d' ' -f3) PROBE_NAME="sysdig-probe" PACKAGE_NAME="sysdig" elif [ "$SCRIPT_NAME" = "sysdigcloud-probe-loader" ]; then SYSDIG_VERSION=$(/opt/draios/bin/dragent --version) PROBE_NAME="sysdigcloud-probe" PACKAGE_NAME="draios-agent" elif [ "$SCRIPT_NAME" = "falco-probe-loader" ]; then SYSDIG_VERSION=$(falco --version | cut -d' ' -f3) PROBE_NAME="falco-probe" PACKAGE_NAME="falco" else echo "This script must be called as sysdig-probe-loader, sysdigcloud-probe-loader, or falco-probe-loader" exit 1 fi echo "* Unloading $PROBE_NAME, if present" rmmod $PROBE_NAME if lsmod | grep $(echo $PROBE_NAME | tr "-" "_") > /dev/null 2>&1; then echo "* $PROBE_NAME seems to still be loaded, hoping the best" exit 0 fi echo "* Running dkms autoinstall" dkms autoinstall --kernelver $KERNEL_RELEASE 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 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 fi echo "* Trying to find precompiled $PROBE_NAME for $KERNEL_RELEASE" if [ -f /proc/config.gz ]; then echo "Found kernel config at /proc/config.gz" HASH=$(zcat /proc/config.gz | md5sum - | cut -d' ' -f1) elif [ -f /boot/config-$KERNEL_RELEASE ]; then echo "Found kernel config at /boot/config-$KERNEL_RELEASE" HASH=$(md5sum /boot/config-$KERNEL_RELEASE | cut -d' ' -f1) 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" HASH=$(md5sum $SYSDIG_HOST_ROOT/boot/config-$KERNEL_RELEASE | cut -d' ' -f1) elif [ -f /usr/lib/ostree-boot/config-$KERNEL_RELEASE ]; then echo "Found kernel config at /usr/lib/ostree-boot/config-$KERNEL_RELEASE" HASH=$(md5sum /usr/lib/ostree-boot/config-$KERNEL_RELEASE | cut -d' ' -f1) 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" HASH=$(md5sum $SYSDIG_HOST_ROOT/usr/lib/ostree-boot/config-$KERNEL_RELEASE | cut -d' ' -f1) fi if [ -z "$HASH" ]; then echo "Cannot find kernel config" exit 1 fi SYSDIG_PROBE_FILENAME="$PROBE_NAME-$SYSDIG_VERSION-$ARCH-$KERNEL_RELEASE-$HASH.ko" if [ -f ~/.sysdig/$SYSDIG_PROBE_FILENAME ]; then echo "Found precompiled module at ~/.sysdig/$SYSDIG_PROBE_FILENAME, loading module" insmod ~/.sysdig/$SYSDIG_PROBE_FILENAME exit $? fi URL=$(echo https://s3.amazonaws.com/download.draios.com/$SYSDIG_REPOSITORY/sysdig-probe-binaries/$SYSDIG_PROBE_FILENAME | sed s/+/%2B/g) echo "* Trying to download precompiled module from $URL" if curl --create-dirs -f -s -o ~/.sysdig/$SYSDIG_PROBE_FILENAME $URL; then echo "Download succeeded, loading module" insmod ~/.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 sysdig-0.19.1/test/000077500000000000000000000000001316537151600140735ustar00rootroot00000000000000sysdig-0.19.1/test/csysdig_trace_regression.sh000066400000000000000000000065551316537151600215250ustar00rootroot00000000000000#!/bin/bash #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.19.1/test/sysdig_batch_parser.sh000077500000000000000000000030151316537151600204500ustar00rootroot00000000000000#!/bin/bash # # 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 $DIRNAMEsysdig-0.19.1/test/sysdig_trace_regression.sh000077500000000000000000000243671316537151600213660ustar00rootroot00000000000000#!/bin/bash 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.19.1/userspace/000077500000000000000000000000001316537151600151065ustar00rootroot00000000000000sysdig-0.19.1/userspace/.gitignore000066400000000000000000000000051316537151600170710ustar00rootroot00000000000000test sysdig-0.19.1/userspace/common/000077500000000000000000000000001316537151600163765ustar00rootroot00000000000000sysdig-0.19.1/userspace/common/sysdig_types.h000066400000000000000000000022521316537151600212760ustar00rootroot00000000000000/* 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 . */ #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.19.1/userspace/libscap/000077500000000000000000000000001316537151600165235ustar00rootroot00000000000000sysdig-0.19.1/userspace/libscap/CMakeLists.txt000066400000000000000000000014301316537151600212610ustar00rootroot00000000000000include_directories("${PROJECT_SOURCE_DIR}/common") include_directories("${ZLIB_INCLUDE}") add_library(scap STATIC scap.c scap_event.c scap_fds.c scap_iflist.c scap_savefile.c scap_procs.c scap_userlist.c flags_table.c dynamic_params_table.c event_table.c syscall_info_table.c) if(USE_BUNDLED_ZLIB) add_dependencies(scap zlib) endif() if (CMAKE_SYSTEM_NAME MATCHES "SunOS") target_link_libraries(scap socket nsl) elseif (WIN32) target_link_libraries(scap Ws2_32.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() endif() sysdig-0.19.1/userspace/libscap/doxygen/000077500000000000000000000000001316537151600202005ustar00rootroot00000000000000sysdig-0.19.1/userspace/libscap/doxygen/conf.dox000066400000000000000000000176061316537151600216530ustar00rootroot00000000000000# 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.19.1/userspace/libscap/doxygen/footer.html000066400000000000000000000000301316537151600223550ustar00rootroot00000000000000 sysdig-0.19.1/userspace/libscap/doxygen/header.html000066400000000000000000000026111316537151600223160ustar00rootroot00000000000000--- layout: default title: sysdig | libscap ---

$projectname $projectnumber

(Generated by Doxygen)

$projectbrief

$projectbrief
$searchbox
sysdig-0.19.1/userspace/libscap/dynamic_params_table.c000066400000000000000000000014621316537151600230300ustar00rootroot00000000000000/* 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 "../common/sysdig_types.h" #include "../../driver/ppm_events_public.h" const struct ppm_param_info ptrace_dynamic_param[PPM_PTRACE_IDX_MAX] = { {{0}, PT_UINT64, PF_HEX}, {{0}, PT_SIGTYPE, PF_DEC}, }; sysdig-0.19.1/userspace/libscap/event_table.c000066400000000000000000001312071316537151600211630ustar00rootroot00000000000000/* 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 "../common/sysdig_types.h" #include "../../driver/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, 4, {{"fd", PT_FD, PF_DEC}, {"name", PT_FSPATH, PF_NA}, {"flags", PT_FLAGS32, PF_HEX, file_flags}, {"mode", PT_UINT32, PF_OCT} } }, /* 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 | EF_DROP_FALCO, 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 | EF_DROP_FALCO, 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 | EF_DROP_FALCO, 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 | EF_DROP_FALCO, 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_NONE, 0}, /* PPME_SOCKET_GETSOCKNAME_X */{"getsockname", EC_NET, EF_NONE, 0}, /* PPME_SOCKET_GETPEERNAME_E */{"getpeername", EC_NET, EF_NONE, 0}, /* PPME_SOCKET_GETPEERNAME_X */{"getpeername", EC_NET, EF_NONE, 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_SOECKETPAIR_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_NONE, 0}, /* PPME_SOCKET_GETSOCKOPT_E */{"getsockopt", EC_NET, EF_NONE, 0}, /* PPME_SOCKET_GETSOCKOPT_X */{"getsockopt", EC_NET, EF_NONE, 0}, /* PPME_SOCKET_SENDMSG_E */{"sendmsg", EC_IO_WRITE, EF_USES_FD | EF_WRITES_TO_FD | EF_MODIFIES_STATE | EF_DROP_FALCO, 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 | EF_DROP_FALCO, 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 | EF_DROP_FALCO, 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SOCKET_RECVMSG_X */{"recvmsg", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE | EF_DROP_FALCO, 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, 3, {{"fd", PT_FD, PF_DEC}, {"name", PT_FSPATH, PF_NA}, {"mode", 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_NONE, 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_NONE, 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, 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, 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_LINK_E */{"link", EC_FILE, EF_NONE, 2, {{"oldpath", PT_FSPATH, PF_NA}, {"newpath", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_LINK_X */{"link", EC_FILE, EF_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_LINKAT_E */{"linkat", EC_FILE, EF_NONE, 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_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_UNLINK_E */{"unlink", EC_FILE, EF_NONE, 1, {{"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_UNLINK_X */{"unlink", EC_FILE, EF_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_UNLINKAT_E */{"unlinkat", EC_FILE, EF_NONE, 2, {{"dirfd", PT_FD, PF_DEC}, {"name", PT_CHARBUF, PF_NA} } }, /* PPME_SYSCALL_UNLINKAT_X */{"unlinkat", EC_FILE, EF_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_PREAD_E */{"pread", EC_IO_READ, EF_USES_FD | EF_READS_FROM_FD, 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, 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, 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, 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_NONE, 1, {{"resource", PT_FLAGS8, PF_DEC, rlimit_resources} } }, /* PPME_SYSCALL_GETRLIMIT_X */{"getrlimit", EC_PROCESS, EF_NONE, 3, {{"res", PT_ERRNO, PF_DEC}, {"cur", PT_INT64, PF_DEC}, {"max", PT_INT64, PF_DEC} } }, /* PPME_SYSCALL_SETRLIMIT_E */{"setrlimit", EC_PROCESS, EF_NONE, 1, {{"resource", PT_FLAGS8, PF_DEC, rlimit_resources} } }, /* PPME_SYSCALL_SETRLIMIT_X */{"setrlimit", EC_PROCESS, EF_NONE, 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_NONE, 1, {{"addr", PT_UINT64, PF_HEX} } }, /* PPME_SYSCALL_BRK_4_X */{"brk", EC_MEMORY, EF_NONE, 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, 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, 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_NONE, 0}, /* PPME_SYSCALL_GETUID_X */ {"getuid", EC_USER, EF_NONE, 1, {{"uid", PT_UID, PF_DEC} } }, /* PPME_SYSCALL_GETEUID_E */ {"geteuid", EC_USER, EF_NONE, 0 }, /* PPME_SYSCALL_GETEUID_X */ {"geteuid", EC_USER, EF_NONE, 1, {{"euid", PT_UID, PF_DEC} } }, /* PPME_SYSCALL_GETGID_E */ {"getgid", EC_USER, EF_NONE, 0}, /* PPME_SYSCALL_GETGID_X */ {"getgid", EC_USER, EF_NONE, 1, {{"gid", PT_GID, PF_DEC} } }, /* PPME_SYSCALL_GETEGID_E */ {"getegid", EC_USER, EF_NONE, 0 }, /* PPME_SYSCALL_GETEGID_X */ {"getegid", EC_USER, EF_NONE, 1, {{"egid", PT_GID, PF_DEC} } }, /* PPME_SYSCALL_GETRESUID_E */ {"getresuid", EC_USER, EF_NONE, 0 }, /* PPME_SYSCALL_GETRESUID_X */ {"getresuid", EC_USER, EF_NONE, 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_NONE, 0 }, /* PPME_SYSCALL_GETRESGID_X */ {"getresgid", EC_USER, EF_NONE, 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_NONE, 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, 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_NONE, 1, {{"semid", PT_INT32, PF_DEC} } }, /* PPME_SYSCALL_SEMOP_X */ {"semop", EC_PROCESS, EF_NONE, 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_NONE, 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_NONE, 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_NONE, 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_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_ACCESS_E */{"access", EC_FILE, EF_NONE, 1, {{"mode", PT_FLAGS32, PF_HEX, access_flags} } }, /* PPME_SYSCALL_ACCESS_X */{"access", EC_FILE, EF_NONE, 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_INTERNAL, EF_SKIPPARSERESET | EF_MODIFIES_STATE, 1, {{"json", PT_CHARBUF, PF_NA} } }, /* PPME_CONTAINER_JSON_X */{"container", EC_INTERNAL, 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_OTHER, 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, 1, {{"filename", PT_FSPATH, PF_NA}} }, /* PPME_SYSCALL_EXECVE_18_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 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} }; sysdig-0.19.1/userspace/libscap/examples/000077500000000000000000000000001316537151600203415ustar00rootroot00000000000000sysdig-0.19.1/userspace/libscap/examples/01-open/000077500000000000000000000000001316537151600215205ustar00rootroot00000000000000sysdig-0.19.1/userspace/libscap/examples/01-open/CMakeLists.txt000066400000000000000000000002201316537151600242520ustar00rootroot00000000000000include_directories("../../../common") include_directories("../../") add_executable(scap-open test.c) target_link_libraries(scap-open scap) sysdig-0.19.1/userspace/libscap/examples/01-open/test.c000066400000000000000000000025611316537151600226470ustar00rootroot00000000000000/* 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 #include uint64_t g_nevts = 0; static void signal_callback(int signal) { printf("events captured: %" PRIu64 "\n", g_nevts); 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; } scap_t* h = scap_open_live(error); if(h == NULL) { fprintf(stderr, "%s\n", error); return -1; } while(1) { res = scap_next(h, &ev, &cpuid); if(res > 0) { fprintf(stderr, "%s\n", scap_getlasterr(h)); scap_close(h); return -1; } if(res != SCAP_TIMEOUT) { g_nevts++; } } scap_close(h); return 0; } sysdig-0.19.1/userspace/libscap/examples/02-validatebuffer/000077500000000000000000000000001316537151600235435ustar00rootroot00000000000000sysdig-0.19.1/userspace/libscap/examples/02-validatebuffer/CMakeLists.txt000066400000000000000000000002431316537151600263020ustar00rootroot00000000000000include_directories("../../../common") include_directories("../..") add_executable(scap-validatebuffer test.c) target_link_libraries(scap-validatebuffer scap) sysdig-0.19.1/userspace/libscap/examples/02-validatebuffer/test.c000066400000000000000000000144051316537151600246720ustar00rootroot00000000000000/* 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 #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); if(h == NULL) { fprintf(stderr, "%s\n", error); return -1; } 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, false, &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.19.1/userspace/libscap/flags_table.c000066400000000000000000000322451316537151600211400ustar00rootroot00000000000000/* 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 "../common/sysdig_types.h" #include "../../driver/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 __LP64__ {"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 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 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 semget_flags[] = { {"IPC_CREAT", PPM_IPC_CREAT}, {"IPC_EXCL", PPM_IPC_EXCL}, {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}, }; sysdig-0.19.1/userspace/libscap/scap-int.h000066400000000000000000000203441316537151600204150ustar00rootroot00000000000000/* 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 . */ //////////////////////////////////////////////////////////////////////////// // Private definitions for the scap library //////////////////////////////////////////////////////////////////////////// #include "settings.h" #ifdef __cplusplus extern "C" { #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_MS 30 #define MAX_N_CONSECUTIVE_WAITS 4 // // Process flags // #define PF_CLONING 1 // // The device descriptor // typedef struct scap_device { int m_fd; char* m_buffer; struct ppm_ring_buffer_info* m_bufinfo; 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 uint32_t m_read_size; // Number of bytes currently ready to be read in this CPU's ring buffer }scap_device; // // 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]; scap_threadinfo* m_proclist; scap_threadinfo m_fake_kernel_proc; uint64_t m_evtcnt; scap_addrlist* m_addrlist; scap_machine_info m_machine_info; scap_userlist* m_userlist; uint32_t m_n_consecutive_waits; 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; }; 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, bool blocking, OUT char** buf, OUT uint32_t* len); // Scan a directory containing process information int32_t scap_proc_scan_proc_dir(scap_t* handle, char* procdirname, int parenttid, int tid_to_scan, struct scap_threadinfo** pi, char *error, bool scan_sockets); // Remove an entry from the process list by parsin 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_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_threadinfo* pi); // Internal helper function to output an fd table void scap_fd_print_fd_table(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 geiven 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_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); // 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, 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); // 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 filedescriptors 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); // 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(struct scap_threadinfo* tinfo, const char* procdirname); // // 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 #ifdef __cplusplus } #endif sysdig-0.19.1/userspace/libscap/scap.c000066400000000000000000001063641316537151600176270ustar00rootroot00000000000000/* 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 #ifndef _WIN32 #include #include #include #include #include #include #include #include #include #endif // _WIN32 #include "scap.h" #ifdef HAS_CAPTURE #include "../../driver/driver_config.h" #endif // HAS_CAPTURE #include "../../driver/ppm_ringbuffer.h" #include "scap_savefile.h" #include "scap-int.h" //#define NDEBUG #include char* scap_getlasterr(scap_t* handle) { return handle->m_lasterr; } #if !defined(HAS_CAPTURE) scap_t* scap_open_live_int(char *error, proc_entry_callback proc_callback, void* proc_callback_context, bool import_users) { snprintf(error, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); 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, proc_entry_callback proc_callback, void* proc_callback_context, bool import_users) { uint32_t j; char filename[SCAP_MAX_PATH_SIZE]; scap_t* handle = NULL; int len; uint32_t ndevs; uint32_t res; uint32_t max_devs; uint32_t all_scanned_devs; // // Allocate the handle // handle = (scap_t*)malloc(sizeof(scap_t)); if(!handle) { snprintf(error, SCAP_LASTERR_SIZE, "error allocating the scap_t structure"); return NULL; } // // Preliminary initializations // memset(handle, 0, sizeof(scap_t)); handle->m_mode = SCAP_MODE_LIVE; // // Find out how many devices we have to open, which equals to the number of CPUs // ndevs = sysconf(_SC_NPROCESSORS_ONLN); max_devs = sysconf(_SC_NPROCESSORS_CONF); // // Allocate the device descriptors. // len = RING_BUF_SIZE * 2; handle->m_devs = (scap_device*)malloc(ndevs * sizeof(scap_device)); if(!handle->m_devs) { scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "error allocating the device handles"); return NULL; } for(j = 0; j < ndevs; j++) { handle->m_devs[j].m_buffer = (char*)MAP_FAILED; 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; // // Create the interface list // if(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(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; // // Open and initialize all the devices // for(j = 0, all_scanned_devs = 0; j < handle->m_ndevs && all_scanned_devs < max_devs; all_scanned_devs++) { // // Open the device // sprintf(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); 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, strerror(errno)); scap_close(handle); 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); 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); return NULL; } // // Additional initializations // handle->m_devs[j].m_lastreadsize = 0; handle->m_devs[j].m_sn_len = 0; handle->m_n_consecutive_waits = 0; scap_stop_dropping_mode(handle); j++; } // // Create the process list // error[0] = '\0'; snprintf(filename, sizeof(filename), "%s/proc", scap_get_host_root()); if((res = scap_proc_scan_proc_dir(handle, filename, -1, -1, NULL, error, true)) != 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 // scap_start_capture(handle); return handle; } #endif // HAS_CAPTURE scap_t* scap_open_offline_int(gzFile gzfile, char *error, proc_entry_callback proc_callback, void* proc_callback_context, bool import_users, uint64_t start_offset) { 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"); 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_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; 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); 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(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; return handle; } scap_t* scap_open_offline(const char* fname, char *error) { gzFile gzfile = gzopen(fname, "rb"); if(gzfile == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "can't open file %s", fname); return NULL; } return scap_open_offline_int(gzfile, error, NULL, NULL, true, 0); } scap_t* scap_open_offline_fd(int fd, char *error) { gzFile gzfile = gzdopen(fd, "rb"); if(gzfile == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "can't open fd %d", fd); return NULL; } return scap_open_offline_int(gzfile, error, NULL, NULL, true, 0); } scap_t* scap_open_live(char *error) { return scap_open_live_int(error, NULL, NULL, true); } scap_t* scap_open_nodriver_int(char *error, 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); 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"); 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 // // Create the interface list // if(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(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(scap_proc_scan_proc_dir(handle, filename, -1, -1, NULL, error, true) != 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) { 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); } return NULL; } return scap_open_offline_int(gzfile, error, args.proc_callback, args.proc_callback_context, args.import_users, args.start_offset); } case SCAP_MODE_LIVE: return scap_open_live_int(error, args.proc_callback, args.proc_callback_context, args.import_users); case SCAP_MODE_NODRIVER: return scap_open_nodriver_int(error, args.proc_callback, args.proc_callback_context, args.import_users); default: 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) uint32_t j; ASSERT(handle->m_file == NULL); if(handle->m_devs != NULL) { // // 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 } 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 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); } // // 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) #ifndef _WIN32 static inline void get_buf_pointers(struct ppm_ring_buffer_info* bufinfo, uint32_t* phead, uint32_t* ptail, uint32_t* pread_size) #else void get_buf_pointers(struct ppm_ring_buffer_info* bufinfo, uint32_t* phead, uint32_t* ptail, uint32_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; } } int32_t scap_readbuf(scap_t* handle, uint32_t cpuid, bool blocking, OUT char** buf, OUT uint32_t* len) { uint32_t thead; uint32_t ttail; uint32_t read_size; // // 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; } // // 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; } bool check_scap_next_wait(scap_t* handle) { uint32_t j; bool res = true; for(j = 0; j < handle->m_ndevs; j++) { uint32_t thead; uint32_t ttail; scap_device* dev = &(handle->m_devs[j]); get_buf_pointers(dev->m_bufinfo, &thead, &ttail, &dev->m_read_size); if(dev->m_read_size > 20000) { handle->m_n_consecutive_waits = 0; res = false; } } if(res == false) { return false; } if(handle->m_n_consecutive_waits >= MAX_N_CONSECUTIVE_WAITS) { handle->m_n_consecutive_waits = 0; return false; } else { return true; } } int32_t refill_read_buffers(scap_t* handle, bool wait) { uint32_t j; uint32_t ndevs = handle->m_ndevs; if(wait) { if(check_scap_next_wait(handle)) { usleep(BUFFER_EMPTY_WAIT_TIME_MS * 1000); handle->m_n_consecutive_waits++; } } // // Refill our data for each of the devices // for(j = 0; j < ndevs; j++) { scap_device* dev = &(handle->m_devs[j]); int32_t res = scap_readbuf(handle, j, false, &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) // // this should be prevented at open time // ASSERT(false); return SCAP_FAILURE; #else uint32_t j; uint64_t max_ts = 0xffffffffffffffffLL; uint64_t max_buf_size = 0; 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) { continue; } // // Make sure that we have data from this ring // if(dev->m_sn_len != 0) { if(dev->m_sn_len > max_buf_size) { max_buf_size = dev->m_sn_len; } // // We want to consume the event with the lowest timestamp // pe = (scap_evt*)dev->m_sn_next_event; 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) { // // Update the pointers. // ASSERT(handle->m_devs[*pcpuid].m_sn_len >= (*pevent)->len); handle->m_devs[*pcpuid].m_sn_len -= (*pevent)->len; handle->m_devs[*pcpuid].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, true); } #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; 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; 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 default: res = SCAP_FAILURE; } if(res == SCAP_SUCCESS) { 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_preemptions = 0; 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 += 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; } return SCAP_SUCCESS; } // // Stop capturing the events // int32_t scap_stop_capture(scap_t* handle) { #if !defined(HAS_CAPTURE) 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(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) 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 // 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) 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, strerror(errno)); ASSERT(false); return SCAP_FAILURE; } } return SCAP_SUCCESS; } #endif #if defined(HAS_CAPTURE) 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(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) 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(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) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #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) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #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) 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(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, false, &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); } static int32_t scap_handle_eventmask(scap_t* handle, uint32_t op, uint32_t event_id) { // // 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(ioctl(handle->m_devs[0].m_fd, op, event_id)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "%s(%d) failed", __FUNCTION__, op); 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, false, &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_clear_eventmask(scap_t* handle) { #if !defined(HAS_CAPTURE) 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) 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) 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) 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(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) 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(ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_DISABLE_DYNAMIC_SNAPLEN)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_enable_dynamic_snaplen failed"); ASSERT(false); return SCAP_FAILURE; } return SCAP_SUCCESS; #endif } const char* scap_get_host_root() { char* p = getenv("SYSDIG_HOST_ROOT"); if(!p) { p = ""; } return p; } bool 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; if(handle->m_driver_procinfo != NULL) { free(handle->m_driver_procinfo); } handle->m_driver_procinfo = (struct ppm_proclist_info*)malloc(memsize); if(handle->m_driver_procinfo == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "driver process list allocation error"); return false; } handle->m_driver_procinfo->max_entries = n_entries; handle->m_driver_procinfo->n_entries = 0; return true; } struct ppm_proclist_info* scap_get_threadlist_from_driver(scap_t* handle) { // // Not supported on files // if(handle->m_mode != SCAP_MODE_LIVE) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_get_threadlist_from_driver not supported on this scap mode"); return NULL; } #if !defined(HAS_CAPTURE) 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(alloc_proclist_info(handle, SCAP_DRIVER_PROCINFO_INITIAL_SIZE) == false) { return NULL; } } int ioctlres = ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_GET_PROCLIST, handle->m_driver_procinfo); if(ioctlres) { if(errno == ENOSPC) { if(alloc_proclist_info(handle, handle->m_driver_procinfo->n_entries + 256) == false) { return NULL; } else { return scap_get_threadlist_from_driver(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) 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(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) { // // 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) snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return SCAP_FAILURE; #else if(ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_GET_N_TRACEPOINT_HIT, ret)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_get_n_tracepoint_hit failed"); ASSERT(false); return SCAP_FAILURE; } return SCAP_SUCCESS; #endif } sysdig-0.19.1/userspace/libscap/scap.def000066400000000000000000000020731316537151600201330ustar00rootroot00000000000000LIBRARY 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_from_driver scap_get_host_root scap_ftell scap_fseeksysdig-0.19.1/userspace/libscap/scap.h000066400000000000000000000714651316537151600176370ustar00rootroot00000000000000/* 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 . */ #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; // // 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 // // Last error string size for scap_open_live() // #define SCAP_LASTERR_SIZE 256 /*! \brief Statisitcs 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_preemptions; ///< Number of preemptions. }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 /*! \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 } 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. 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; UT_hash_handle hh; ///< makes this structure hashable }scap_threadinfo; typedef void (*proc_entry_callback)(void* context, int64_t tid, scap_threadinfo* tinfo, scap_fdinfo* fdinfo, scap_t* newhandle); /*! \brief Arguments for scap_open */ typedef enum { SCAP_MODE_CAPTURE, SCAP_MODE_LIVE, 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. }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 fututre use uint64_t reserved2; ///< reserved for fututre use uint64_t reserved3; ///< reserved for fututre use uint64_t reserved4; ///< reserved for fututre 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 { 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 backword 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 { 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. \return The capture instance handle in case of success. NULL in case of failure. */ scap_t* scap_open_live(char *error); /*! \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. \return The capture instance handle in case of success. NULL in case of failure. */ scap_t* scap_open_offline(const char* fname, char *error); /*! \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. \return The capture instance handle in case of success. NULL in case of failure. */ scap_t* scap_open_offline_fd(int fd, char *error); /*! \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. \return The capture instance handle in case of success. NULL in case of failure. */ scap_t* scap_open(scap_open_args args, char *error); /*! \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. */ 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 succesful 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 tracefile for writing \param handle Handle to the capture instance. \param fname The name of the tracefile. \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); /*! \brief Open a tracefile 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 tracefile. \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 tracefile. \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 tracefile. 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 succesful. 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 succesful. 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 succesful. 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 succesful. 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 succesful. 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 by querying the sysdig kernel module. */ struct ppm_proclist_info* scap_get_threadlist_from_driver(scap_t* handle); /*@}*/ /////////////////////////////////////////////////////////////////////////////// // 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, bool blocking, 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); 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_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_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); int32_t scap_enable_simpledriver_mode(scap_t* handle); int32_t scap_get_n_tracepoint_hit(scap_t* handle, long* ret); #ifdef __cplusplus } #endif sysdig-0.19.1/userspace/libscap/scap.vcxproj000066400000000000000000000150551316537151600210740ustar00rootroot00000000000000 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.19.1/userspace/libscap/scap.vcxproj.filters000066400000000000000000000051511316537151600225370ustar00rootroot00000000000000 {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.19.1/userspace/libscap/scap_event.c000066400000000000000000000033301316537151600210150ustar00rootroot00000000000000/* 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 #ifdef _WIN32 #include #else #include #include #endif // _WIN32 #include "scap.h" #include "scap-int.h" // This is defined in the driver extern const struct ppm_event_info g_event_info[]; extern const struct ppm_syscall_desc g_syscall_info_table[]; extern bool validate_info_table_size(); // // 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.19.1/userspace/libscap/scap_fds.c000066400000000000000000001271401316537151600204560ustar00rootroot00000000000000/* 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 #include "scap.h" #include "scap-int.h" #include #include #include #include "uthash.h" #ifdef _WIN32 #include #elif defined(__APPLE__) #include #include #include #include #else #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include #include #if defined(__linux__) #include #include //#include //#include #endif #endif #define SOCKET_SCAN_BUFFER_SIZE 1024 * 1024 int32_t scap_fd_print_ipv6_socket_info(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)) { return SCAP_FAILURE; } if(NULL == inet_ntop(AF_INET6,fdi->info.ipv6info.dip,destination_address,100)) { 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_fdinfo *fdi, OUT char *str, uint32_t stlen) { char address[100]; if(NULL == inet_ntop(AF_INET6,fdi->info.ipv6serverinfo.ip,address,100)) { 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_fdinfo *fdi, OUT char *str, uint32_t stlen) { // // Input validation // if((fdi)->type == SCAP_FD_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(fdi,str,stlen); break; case SCAP_FD_IPV6_SERVSOCK: return scap_fd_print_ipv6_server_socket_info(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); 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) { uint32_t res = 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; 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) { uint8_t type = (uint8_t)fdi->type; uint16_t stlen; if(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; } 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, gzFile f) { uint8_t type; uint32_t res = SCAP_SUCCESS; *nbytes = 0; if(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)) { return SCAP_FAILURE; } 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); 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; } 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(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) { 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(handle->m_lasterr, SCAP_LASTERR_SIZE, "process table allocation error (2)"); return SCAP_FAILURE; } } else { handle->m_proc_callback(handle->m_proc_callback_context, tinfo->tid, tinfo, fdi, handle); } return SCAP_SUCCESS; } #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) { 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); } 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; } void scap_fd_flags_file(scap_t *handle, scap_fdinfo *fdi, const char *procdir) { int is_first_line = true; const char *delimiters = " \t"; 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/%ld", procdir, fdi->fd); finfo = fopen(fd_dir_name, "r"); if(finfo == NULL) { return; } while(fgets(line, sizeof(line), finfo) != NULL) { // We are just interested in the flags. // // The format of the file is: // pos: XXXX // flags: YYYYYYYY // mnt_id: ZZZ char *scratch; char *token; if(is_first_line) { is_first_line = false; continue; } token = strtok_r(line, delimiters, &scratch); if(token == NULL) { ASSERT(false); continue; } token = strtok_r(NULL, delimiters, &scratch); if(token == NULL) { ASSERT(false); continue; } uint32_t open_flags; unsigned long flags = strtoul(token, 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; break; } 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); } 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; HASH_ADD_INT64(*sockets_by_ns, net_ns, sockets); if(uth_status != SCAP_SUCCESS) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "socket list allocation error"); return SCAP_FAILURE; } if(scap_fd_read_sockets(handle, procdir, sockets) == SCAP_FAILURE) { 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); } // // 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); } 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); 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 allocatiallocation error"); 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); 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"); 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); 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; // TODO: set some error message 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); 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; // TODO: set some error message 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 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); 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); 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); 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); 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); return SCAP_FAILURE; } snprintf(filename, sizeof(filename), "%stcp6", netroot); /* We assume if there is /proc/net/tcp6 that ipv6 is avaiable */ 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); return SCAP_FAILURE; } snprintf(filename, sizeof(filename), "%sudp6", netroot); 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); return SCAP_FAILURE; } snprintf(filename, sizeof(filename), "%sraw6", netroot); 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); 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) { 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) { 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) { 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) { 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) { 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_threadinfo *tinfo) { scap_fd_print_fd_table(tinfo->fdlist); } void scap_fd_print_fd_table(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(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.19.1/userspace/libscap/scap_iflist.c000066400000000000000000000141171316537151600211730ustar00rootroot00000000000000/* 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 "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; } 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; } 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); } 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); } 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.19.1/userspace/libscap/scap_procs.c000066400000000000000000000545641316537151600210410ustar00rootroot00000000000000/* 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 #ifndef _WIN32 #include #include #include #include #include #include #include #endif #include "scap.h" #include "../../driver/ppm_ringbuffer.h" #include "scap-int.h" #if defined(HAS_CAPTURE) int32_t scap_proc_fill_cwd(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) { return SCAP_FAILURE; } tinfo->cwd[target_res] = '\0'; return SCAP_SUCCESS; } int32_t scap_proc_fill_info_from_stats(char* procdirname, struct scap_threadinfo* tinfo) { char filename[SCAP_MAX_PATH_SIZE]; uint32_t nfound = 0; int64_t tmp; uint32_t uid; uint64_t ppid; uint64_t vpid; uint64_t vtid; int64_t sid; 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->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); return SCAP_FAILURE; } while(fgets(line, sizeof(line), f) != NULL) { 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, "NStgid:") == line) { nfound++; if(sscanf(line, "NStgid: %*u %" PRIu64, &vpid) == 1) { tinfo->vpid = vpid; } else { tinfo->vpid = tinfo->pid; } } if(nfound == 8) { break; } } ASSERT(nfound == 8 || nfound == 6 || nfound == 5); fclose(f); snprintf(filename, sizeof(filename), "%sstat", procdirname); f = fopen(filename, "r"); if(f == NULL) { ASSERT(false); return SCAP_FAILURE; } if(fgets(line, sizeof(line), f) == NULL) { ASSERT(false); fclose(f); return SCAP_FAILURE; } s = strrchr(line, ')'); if(s == NULL) { ASSERT(false); fclose(f); return SCAP_FAILURE; } // // Extract the line content // if(sscanf(s + 2, "%c %" PRId64 " %" PRId64 " %" PRId64 " %" PRId32 " %" PRId64 " %" PRId64 " %" PRId64 " %" PRId64 " %" PRId64, &tmpc, &tmp, &tmp, &sid, &tty, &tmp, &tmp, &pfminor, &tmp, &pfmajor) != 10) { ASSERT(false); fclose(f); return SCAP_FAILURE; } tinfo->pfmajor = pfmajor; tinfo->pfminor = pfminor; tinfo->sid = (uint64_t) sid; 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(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(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); 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); return SCAP_FAILURE; } // subsys subsys_list = strtok_r(NULL, ":", &scratch); if(subsys_list == NULL) { ASSERT(false); fclose(f); 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; } // transient cgroup if(strncmp(subsys_list, "name=", sizeof("name=") - 1) == 0) { continue; } // cgroup cgroup = strtok_r(NULL, ":", &scratch); if(cgroup == NULL) { ASSERT(false); fclose(f); 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) { return SCAP_FAILURE; } #if !defined(HAS_CAPTURE) ASSERT(false) return SCAP_FAILURE; #else *vtid = ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_GET_VTID, tid); if(*vtid == -1) { ASSERT(false); 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) { return SCAP_FAILURE; } #if !defined(HAS_CAPTURE) ASSERT(false) return SCAP_FAILURE; #else *vpid = ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_GET_VPID, tid); if(*vpid == -1) { ASSERT(false); return SCAP_FAILURE; } return SCAP_SUCCESS; #endif } int32_t scap_getpid_global(scap_t* handle, int64_t* pid) { if(handle->m_mode != SCAP_MODE_LIVE) { ASSERT(false); return SCAP_FAILURE; } #if !defined(HAS_CAPTURE) ASSERT(false) return SCAP_FAILURE; #else *pid = ioctl(handle->m_devs[0].m_fd, PPM_IOCTL_GET_CURRENT_PID); if(*pid == -1) { ASSERT(false); return SCAP_FAILURE; } return SCAP_SUCCESS; #endif } int32_t scap_proc_fill_root(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 { 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, int parenttid, int tid_to_scan, 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 return SCAP_FAILURE; } tinfo->tid = tid; if(parenttid != -1) { tinfo->pid = parenttid; } else { tinfo->pid = tid; } tinfo->fdlist = NULL; // // 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; } // // 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", filename); 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", filename); fclose(f); free(tinfo); return SCAP_FAILURE; } line[SCAP_MAX_PATH_SIZE - 1] = 0; sscanf(line, "Name:%s", tinfo->comm); fclose(f); } // // 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 %s", filename); 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 %s", filename); 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(dir_name, tinfo)) { snprintf(error, SCAP_LASTERR_SIZE, "can't fill cwd for %s", dir_name); free(tinfo); return SCAP_FAILURE; } // // extract the user id and ppid from /proc/pid/status // if(SCAP_FAILURE == scap_proc_fill_info_from_stats(dir_name, tinfo)) { snprintf(error, SCAP_LASTERR_SIZE, "can't fill cwd for %s", dir_name); free(tinfo); return SCAP_FAILURE; } // // Set the file limit // if(SCAP_FAILURE == scap_proc_fill_flimit(tinfo->tid, tinfo)) { snprintf(error, SCAP_LASTERR_SIZE, "can't fill flimit for %s", dir_name); free(tinfo); return SCAP_FAILURE; } if(scap_proc_fill_cgroups(tinfo, dir_name) == SCAP_FAILURE) { snprintf(error, SCAP_LASTERR_SIZE, "can't fill cgroups for %" PRIu64, tinfo->tid); 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(tinfo, dir_name)) { snprintf(error, SCAP_LASTERR_SIZE, "can't fill root for %s", dir_name); 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_to_scan is set we assume this is a runtime lookup so no // need to use the table // if(tid_to_scan == -1) { // // 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, tinfo->tid, tinfo, NULL, handle); free_tinfo = true; } } else { *procinfo = tinfo; } // // Only add fds for processes, not threads // if(parenttid == -1) { res = scap_fd_scan_fd_dir(handle, dir_name, tinfo, sockets_by_ns, error); } if(free_tinfo) { free(tinfo); } return res; } // // Scan a directory containing multiple processes under /proc // int32_t scap_proc_scan_proc_dir(scap_t* handle, char* procdirname, int parenttid, int tid_to_scan, struct scap_threadinfo** procinfo, char *error, bool scan_sockets) { 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; tid = 0; dir_p = opendir(procdirname); if(dir_p == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "error opening the %s directory", procdirname); return SCAP_NOTFOUND; } if(-1 == parenttid) { if(!scan_sockets) { sockets_by_ns = (void*)-1; } } if(tid_to_scan != -1) { *procinfo = NULL; } 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; } // // if tid_to_scan is set we assume is a runtime lookup so no // need to use the table // if(tid_to_scan == -1) { 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; } } if(tid_to_scan == -1 || tid_to_scan == tid) { // // We have a process that needs to be explored // res = scap_proc_add_from_proc(handle, tid, parenttid, tid_to_scan, procdirname, &sockets_by_ns, procinfo, error); if(res != SCAP_SUCCESS) { snprintf(error, SCAP_LASTERR_SIZE, "cannot add procs tid = %"PRIu64", parenttid = %"PRIi32", dirname = %s", tid, parenttid, procdirname); break; } if(tid_to_scan != -1) { // // procinfo should be filled, except when // the main thread already terminated and // the various proc files were not readable // // ASSERT(*procinfo); 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(handle, childdir, tid, tid_to_scan, procinfo, error, scan_sockets) == SCAP_FAILURE) { res = SCAP_FAILURE; break; } } if(tid_to_scan != -1 && *procinfo) { // // We found the process we were looking for, no need to keep iterating // 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; } #endif // HAS_CAPTURE // // 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_scan_proc_dir(handle, filename, -1, 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, -1, -1, NULL, handle->m_lasterr, true); } 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 { return SCAP_FAILURE; } } int32_t scap_fd_add(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 { return SCAP_FAILURE; } } // // Internal helper functions to output the process table to screen // void scap_proc_print_info(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(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(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(tinfo); } printf("*******************************************\n"); } sysdig-0.19.1/userspace/libscap/scap_savefile.c000077500000000000000000001671071316537151600215120ustar00rootroot00000000000000/* 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 . */ #ifndef _WIN32 #include #endif #include #include #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; } } } 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 struct scap_fdinfo *fdi; struct scap_fdinfo *tfdi; // // First pass of the table to calculate the length // HASH_ITER(hh, tinfo->fdlist, fdi, tfdi) { if(fdi->type != SCAP_FD_UNINITIALIZED && fdi->type != SCAP_FD_UNKNOWN) { totlen += scap_fd_info_len(fdi); } } // // Create the block // bh.block_type = FDL_BLOCK_TYPE; 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 (fd1)"); return SCAP_FAILURE; } // // Write the tid // if(scap_dump_write(d, &tinfo->tid, sizeof(tinfo->tid)) != sizeof(tinfo->tid)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fd2)"); return SCAP_FAILURE; } // // Second pass pass of the table to dump it // HASH_ITER(hh, tinfo->fdlist, fdi, tfdi) { if(fdi->type != SCAP_FD_UNINITIALIZED) { if(scap_fd_write_to_disk(handle, fdi, d) != SCAP_SUCCESS) { return SCAP_FAILURE; } } } // // 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_V7; 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_V7; 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) { uint16_t commlen; uint16_t exelen; uint16_t exepathlen; uint16_t argslen; uint16_t cwdlen; uint16_t rootlen; // // Second pass of the table to dump it // commlen = (uint16_t)strnlen(tinfo->comm, SCAP_MAX_PATH_SIZE); exelen = (uint16_t)strnlen(tinfo->exe, SCAP_MAX_PATH_SIZE); exepathlen = (uint16_t)strnlen(tinfo->exepath, SCAP_MAX_PATH_SIZE); argslen = tinfo->args_len; cwdlen = (uint16_t)strnlen(tinfo->cwd, SCAP_MAX_PATH_SIZE); rootlen = (uint16_t)strnlen(tinfo->root, SCAP_MAX_PATH_SIZE); if(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, &commlen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, tinfo->comm, commlen) != commlen || scap_dump_write(d, &exelen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, tinfo->exe, exelen) != exelen || scap_dump_write(d, &exepathlen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, tinfo->exepath, exepathlen) != exepathlen || scap_dump_write(d, &argslen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, tinfo->args, argslen) != argslen || scap_dump_write(d, &cwdlen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, tinfo->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, &(tinfo->env_len), sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, tinfo->env, tinfo->env_len) != tinfo->env_len || 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, &(tinfo->cgroups_len), sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, tinfo->cgroups, tinfo->cgroups_len) != tinfo->cgroups_len || scap_dump_write(d, &rootlen, sizeof(uint16_t)) != sizeof(uint16_t) || scap_dump_write(d, tinfo->root, rootlen) != rootlen) { 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; struct scap_threadinfo *tinfo; struct scap_threadinfo *ttinfo; // // First pass pass of the table to calculate the length // HASH_ITER(hh, handle->m_proclist, tinfo, ttinfo) { if(!tinfo->filtered_out) { uint32_t il= (uint32_t) (sizeof(uint64_t) + // tid sizeof(uint64_t) + // pid sizeof(uint64_t) + // ptid sizeof(uint64_t) + // sid 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) + // 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 + sizeof(uint32_t) + 2 + strnlen(tinfo->root, SCAP_MAX_PATH_SIZE)); totlen += il; } } if(scap_write_proclist_header(handle, d, totlen) != SCAP_SUCCESS) { 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) != SCAP_SUCCESS) { return SCAP_FAILURE; } } 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; bh.block_total_length = scap_normalize_block_len(sizeof(block_header) + 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, entry, entrylen) != entrylen) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (IF2)"); return SCAP_FAILURE; } totlen += 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, entry, entrylen) != entrylen) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (IF2)"); return SCAP_FAILURE; } totlen += 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; } // // Calculate the block length // 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); totlen += sizeof(type) + sizeof(info->uid) + sizeof(info->gid) + sizeof(uint16_t) + namelen + sizeof(uint16_t) + homedirlen + sizeof(uint16_t) + shelllen; } 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); totlen += sizeof(type) + sizeof(info->gid) + sizeof(uint16_t) + namelen; } // // Create the block // bh.block_type = UL_BLOCK_TYPE; 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 (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, &(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) { 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, &(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) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (U2)"); return SCAP_FAILURE; } } // // 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, -1, -1, NULL, handle->m_lasterr, true) != 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) { 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, false); } // // 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; 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; 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 totreadsize = 0; size_t padding_len; struct scap_threadinfo tinfo; uint16_t stlen; uint32_t padding; int32_t uth_status = SCAP_SUCCESS; struct scap_threadinfo *ntinfo; 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.clone_ts = 0; tinfo.tty = 0; tinfo.exepath[0] = 0; while(((int32_t)block_length - (int32_t)totreadsize) >= 4) { // // tid // readsize = gzread(f, &(tinfo.tid), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); totreadsize += readsize; // // pid // readsize = gzread(f, &(tinfo.pid), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); totreadsize += readsize; // // ptid // readsize = gzread(f, &(tinfo.ptid), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); totreadsize += 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: readsize = gzread(f, &(tinfo.sid), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); totreadsize += 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; } totreadsize += readsize; readsize = gzread(f, tinfo.comm, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file tinfo.comm[stlen] = 0; totreadsize += 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; } totreadsize += readsize; readsize = gzread(f, tinfo.exe, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file tinfo.exe[stlen] = 0; totreadsize += 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: // // 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; } totreadsize += readsize; readsize = gzread(f, tinfo.exepath, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file tinfo.exepath[stlen] = 0; totreadsize += 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; } totreadsize += 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; totreadsize += 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; } totreadsize += readsize; readsize = gzread(f, tinfo.cwd, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file tinfo.cwd[stlen] = 0; totreadsize += readsize; // // fdlimit // readsize = gzread(f, &(tinfo.fdlimit), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); totreadsize += readsize; // // flags // readsize = gzread(f, &(tinfo.flags), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); totreadsize += readsize; // // uid // readsize = gzread(f, &(tinfo.uid), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); totreadsize += readsize; // // gid // readsize = gzread(f, &(tinfo.gid), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); totreadsize += 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: // // vmsize_kb // readsize = gzread(f, &(tinfo.vmsize_kb), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); totreadsize += readsize; // // vmrss_kb // readsize = gzread(f, &(tinfo.vmrss_kb), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); totreadsize += readsize; // // vmswap_kb // readsize = gzread(f, &(tinfo.vmswap_kb), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); totreadsize += readsize; // // pfmajor // readsize = gzread(f, &(tinfo.pfmajor), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); totreadsize += readsize; // // pfminor // readsize = gzread(f, &(tinfo.pfminor), sizeof(uint64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); totreadsize += 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) { // // 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; } totreadsize += 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; totreadsize += 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) { // // vtid // readsize = gzread(f, &(tinfo.vtid), sizeof(int64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); totreadsize += readsize; // // vpid // readsize = gzread(f, &(tinfo.vpid), sizeof(int64_t)); CHECK_READ_SIZE(readsize, sizeof(uint64_t)); totreadsize += 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; totreadsize += readsize; readsize = gzread(f, tinfo.cgroups, stlen); CHECK_READ_SIZE(readsize, stlen); totreadsize += readsize; if(block_type == PL_BLOCK_TYPE_V5 || block_type == PL_BLOCK_TYPE_V6 || block_type == PL_BLOCK_TYPE_V7) { 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; } totreadsize += readsize; readsize = gzread(f, tinfo.root, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file tinfo.root[stlen] = 0; totreadsize += readsize; } } break; default: snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted process block type (fd1)"); ASSERT(false); return SCAP_FAILURE; } // // 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. // 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, tinfo.tid, &tinfo, NULL, handle); } } // // 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_proclist read more %lu than a block %u", totreadsize, block_length); 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) { 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; } 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 { ASSERT(false); snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "trace file has corrupted interface list(1)"); res = SCAP_FAILURE; goto scap_read_iflist_error; } if(toread < entrysize) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "trace file has corrupted interface list(2)"); 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; 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; if(toread < 4) { break; } 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(iftype == SCAP_II_IPV4) { entrysize = sizeof(scap_ifinfo_ipv4) + ifnamlen - SCAP_MAX_PATH_SIZE; if(toread < entrysize) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "trace file has corrupted interface list(1)"); res = SCAP_FAILURE; goto scap_read_iflist_error; } // Copy the entry memcpy(handle->m_addrlist->v4list + ifcnt4, pif, entrysize); // Make sure the name string is NULL-terminated *((char *)(handle->m_addrlist->v4list + ifcnt4) + entrysize) = 0; pif += entrysize; totreadsize += entrysize; ifcnt4++; } else if(iftype == SCAP_II_IPV4_NOLINKSPEED) { scap_ifinfo_ipv4_nolinkspeed* src; scap_ifinfo_ipv4* dst; entrysize = sizeof(scap_ifinfo_ipv4_nolinkspeed) + ifnamlen - SCAP_MAX_PATH_SIZE; if(toread < entrysize) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "trace file has corrupted interface list(1)"); 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; pif += entrysize; totreadsize += entrysize; ifcnt4++; } else if(iftype == SCAP_II_IPV6) { entrysize = sizeof(scap_ifinfo_ipv6) + ifnamlen - SCAP_MAX_PATH_SIZE; if(toread < entrysize) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "trace file has corrupted interface list(1)"); res = SCAP_FAILURE; goto scap_read_iflist_error; } // Copy the entry memcpy(handle->m_addrlist->v6list + ifcnt6, pif, entrysize); // Make sure the name string is NULL-terminated *((char *)(handle->m_addrlist->v6list + ifcnt6) + entrysize) = 0; pif += entrysize; totreadsize += entrysize; ifcnt6++; } else if(iftype == SCAP_II_IPV6_NOLINKSPEED) { scap_ifinfo_ipv6_nolinkspeed* src; scap_ifinfo_ipv6* dst; entrysize = sizeof(scap_ifinfo_ipv6_nolinkspeed) + ifnamlen - SCAP_MAX_PATH_SIZE; if(toread < entrysize) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "trace file has corrupted interface list(1)"); 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; pif += entrysize; totreadsize += entrysize; 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; } } // // 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) { size_t readsize; size_t totreadsize = 0; size_t padding_len; uint32_t padding; uint8_t type; uint16_t stlen; // // 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) { // // type // readsize = gzread(f, &(type), sizeof(type)); CHECK_READ_SIZE(readsize, sizeof(type)); totreadsize += 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)); totreadsize += readsize; // // gid // readsize = gzread(f, &(puser->gid), sizeof(uint32_t)); CHECK_READ_SIZE(readsize, sizeof(uint32_t)); totreadsize += 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; } totreadsize += readsize; readsize = gzread(f, puser->name, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file puser->name[stlen] = 0; totreadsize += 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; } totreadsize += readsize; readsize = gzread(f, puser->homedir, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file puser->homedir[stlen] = 0; totreadsize += 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; } totreadsize += readsize; readsize = gzread(f, puser->shell, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file puser->shell[stlen] = 0; totreadsize += readsize; } 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)); totreadsize += 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; } totreadsize += readsize; readsize = gzread(f, pgroup->name, stlen); CHECK_READ_SIZE(readsize, stlen); // the string is not null-terminated on file pgroup->name[stlen] = 0; totreadsize += readsize; } } // // 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) { 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, 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, tid, NULL, &fdi, handle); } } // // 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; } // // 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_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: found_fdl = 1; if(scap_read_fdlist(handle, f, bh.block_total_length - sizeof(block_header) - 4) != SCAP_SUCCESS) { return SCAP_FAILURE; } break; case EV_BLOCK_TYPE: case EV_BLOCK_TYPE_INT: case EVF_BLOCK_TYPE: 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: found_il = 1; if(scap_read_iflist(handle, f, bh.block_total_length - sizeof(block_header) - 4) != SCAP_SUCCESS) { return SCAP_FAILURE; } break; case UL_BLOCK_TYPE: case UL_BLOCK_TYPE_INT: found_ul = 1; if(scap_read_userlist(handle, f, bh.block_total_length - sizeof(block_header) - 4) != 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; gzFile f = handle->m_file; ASSERT(f != NULL); // // 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_INT && bh.block_type != EVF_BLOCK_TYPE) { 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; } if(bh.block_total_length < sizeof(bh) + sizeof(struct ppm_evt_hdr) + 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) { 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)); } 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.19.1/userspace/libscap/scap_savefile.h000066400000000000000000000117771316537151600215150ustar00rootroot00000000000000/* 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 . */ // 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. #define CURRENT_MAJOR_VERSION 1 // Minor version of the file format supported by this library. #define CURRENT_MINOR_VERSION 0 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; /////////////////////////////////////////////////////////////////////////////// // 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 /////////////////////////////////////////////////////////////////////////////// // 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 /////////////////////////////////////////////////////////////////////////////// // 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 /////////////////////////////////////////////////////////////////////////////// // 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 /////////////////////////////////////////////////////////////////////////////// // 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 /////////////////////////////////////////////////////////////////////////////// // EVENT BLOCK WITH FLAGS /////////////////////////////////////////////////////////////////////////////// #define EVF_BLOCK_TYPE 0x208 #if defined __sun #pragma pack() #else #pragma pack(pop) #endif sysdig-0.19.1/userspace/libscap/scap_userlist.c000066400000000000000000000102751316537151600215540ustar00rootroot00000000000000/* 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 "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 // p = getpwent(); for(usercnt = 0; p; p = getpwent(), usercnt++); endpwent(); 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 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 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.19.1/userspace/libscap/settings.h000066400000000000000000000003421316537151600205330ustar00rootroot00000000000000// // 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 20sysdig-0.19.1/userspace/libscap/stdint_win.h000066400000000000000000000170601316537151600210620ustar00rootroot00000000000000// 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.19.1/userspace/libscap/syscall_info_table.c000066400000000000000000000757621316537151600225440ustar00rootroot00000000000000/* 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 "../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_NONE), "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_NONE), "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_NONE), "alarm" }, /*PPM_SC_FSTAT*/ { EC_FILE, (enum ppm_event_flags)(EF_DROP_FALCO), "fstat" }, /*PPM_SC_PAUSE*/ { EC_WAIT, (enum ppm_event_flags)(EF_NONE), "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_NONE), "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_NONE), "times" }, /*PPM_SC_BRK*/ { EC_MEMORY, (enum ppm_event_flags)(EF_NONE), "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_NONE), "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 pathnames beginning with /. The root directory is inherited by all children of the calling process. */ /*PPM_SC_USTAT*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "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_NONE), "getppid" }, /*PPM_SC_GETPGRP*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "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_NONE), "setrlimit" }, /* get/set resource (CPU, FDs, memory...) limits */ /*PPM_SC_GETRUSAGE*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "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_NONE), "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_NONE), "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_NONE), "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_NONE), "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_NONE), "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_NONE), "getsid" }, /* returns the session ID of the calling process */ /*PPM_SC_FDATASYNC*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "fdatasync" }, /* synchronize a file's in-core state with storage device */ /*PPM_SC_MLOCK*/ { EC_MEMORY, (enum ppm_event_flags)(EF_NONE), "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_NONE), "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_NONE), "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_NONE), "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_NONE), "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_NONE), "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_NONE), "sched_get_priority_max" }, /*PPM_SC_SCHED_GET_PRIORITY_MIN*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "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_NONE), "nanosleep" }, /*PPM_SC_MREMAP*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "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_NONE), "rt_sigaction" }, /*PPM_SC_RT_SIGPROCMASK*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_NONE), "rt_sigprocmask" }, /*PPM_SC_RT_SIGPENDING*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_NONE), "rt_sigpending" }, /*PPM_SC_RT_SIGTIMEDWAIT*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_NONE), "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_NONE), "rt_sigsuspend" }, /*PPM_SC_GETCWD*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "getcwd" }, /*PPM_SC_CAPGET*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "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_NONE), "getrlimit" }, /*PPM_SC_LCHOWN*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "lchown" }, /*PPM_SC_GETUID*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "getuid" }, /*PPM_SC_GETGID*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "getgid" }, /*PPM_SC_GETEUID*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "geteuid" }, /*PPM_SC_GETEGID*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "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_NONE), "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_NONE), "getresuid" }, /*PPM_SC_SETRESGID*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "setresgid" }, /*PPM_SC_GETRESGID*/ { EC_USER, (enum ppm_event_flags)(EF_NONE), "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_NONE), "mincore" }, /* determine whether pages are resident in memory */ /*PPM_SC_MADVISE*/ { EC_MEMORY, (enum ppm_event_flags)(EF_NONE), "madvise" }, /* give advice about use of memory */ /*PPM_SC_GETTID*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "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_NONE), "getxattr" }, /*PPM_SC_LGETXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "lgetxattr" }, /*PPM_SC_FGETXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "fgetxattr" }, /*PPM_SC_LISTXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "listxattr" }, /*PPM_SC_LLISTXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "llistxattr" }, /*PPM_SC_FLISTXATTR*/ { EC_FILE, (enum ppm_event_flags)(EF_NONE), "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_NONE), "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_NONE), "get_thread_area" }, /*PPM_SC_IO_SETUP*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "io_setup" }, /* create an asynchronous I/O context (for libaio) */ /*PPM_SC_IO_DESTROY*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "io_destroy" }, /*PPM_SC_IO_GETEVENTS*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "io_getevents" }, /*PPM_SC_IO_SUBMIT*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "io_submit" }, /*PPM_SC_IO_CANCEL*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "io_cancel" }, /*PPM_SC_EXIT_GROUP*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "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_NONE), "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_NONE), "mq_timedsend" }, /*PPM_SC_MQ_TIMEDRECEIVE*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "mq_timedreceive" }, /*PPM_SC_MQ_NOTIFY*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "mq_notify" }, /*PPM_SC_MQ_GETSETATTR*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "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_NONE), "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_NONE), "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_NONE), "vmsplice" }, /* splice user pages into a pipe */ /*PPM_SC_GETCPU*/ { EC_PROCESS, (enum ppm_event_flags)(EF_NONE), "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_NONE), "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_NONE), "signalfd4" }, /* create a pollable file descriptor for accepting signals */ /*PPM_SC_EVENTFD2*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "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_NONE), "getsockname" }, /*PPM_SC_GETPEERNAME*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "getpeername" }, /*PPM_SC_SOCKETPAIR*/ { EC_NET, (enum ppm_event_flags)(EF_NONE), "socketpair" }, /*PPM_SC_SENDTO*/ { EC_NET, (enum ppm_event_flags)(EF_DROP_FALCO), "sendto" }, /*PPM_SC_RECVFROM*/ { EC_NET, (enum ppm_event_flags)(EF_DROP_FALCO), "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_NONE), "getsockopt" }, /*PPM_SC_SENDMSG*/ { EC_NET, (enum ppm_event_flags)(EF_DROP_FALCO), "sendmsg" }, /*PPM_SC_SENDMMSG*/ { EC_NET, (enum ppm_event_flags)(EF_DROP_FALCO), "sendmmsg" }, /*PPM_SC_RECVMSG*/ { EC_NET, (enum ppm_event_flags)(EF_DROP_FALCO), "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_NONE), "semop" }, /*PPM_SC_SEMGET*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "semget" }, /*PPM_SC_SEMCTL*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "semctl" }, /*PPM_SC_MSGSND*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "msgsnd" }, /*PPM_SC_MSGRCV*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "msgrcv" }, /*PPM_SC_MSGGET*/ { EC_IPC, (enum ppm_event_flags)(EF_NONE), "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_NONE), "ugetrlimit" }, /*PPM_SC_BDFLUSH*/ { EC_OTHER, (enum ppm_event_flags)(EF_NONE), "bdflush" }, /* deprecated */ /*PPM_SC_SIGPROCMASK*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_NONE), "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_NONE), "newselect" }, /*PPM_SC_SGETMASK*/ { EC_SIGNAL, (enum ppm_event_flags)(EF_NONE), "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_NONE), "sigpending" }, /* examine pending signals */ /*PPM_SC_OLDUNAME*/ { EC_SYSTEM, (enum ppm_event_flags)(EF_NONE), "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_NONE), "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_NONE), "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_NONE), "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_NONE), "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_NONE), "geteuid" }, /*PPM_SC_GETGID32*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "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_NONE), "setresgid" }, /*PPM_SC_GETRESUID32*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "getresuid" }, /*PPM_SC_GETRESGID32*/ { EC_IO_OTHER, (enum ppm_event_flags)(EF_NONE), "getresgid" }, }; bool validate_info_table_size() { return (sizeof(g_syscall_info_table) / sizeof(g_syscall_info_table[0]) == PPM_SC_MAX); } sysdig-0.19.1/userspace/libscap/uthash.h000066400000000000000000001643651316537151600202070ustar00rootroot00000000000000/* 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 deletee, 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 deletee (users) * in the patch-up process. Solution: use scratch space to * copy the deletee 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.19.1/userspace/libsinsp/000077500000000000000000000000001316537151600167315ustar00rootroot00000000000000sysdig-0.19.1/userspace/libsinsp/.gitignore000066400000000000000000000000121316537151600207120ustar00rootroot00000000000000unit_testssysdig-0.19.1/userspace/libsinsp/CMakeLists.txt000066400000000000000000000045371316537151600215020ustar00rootroot00000000000000include_directories(./) include_directories(../../common) include_directories(../libscap) include_directories("${JSONCPP_INCLUDE}") include_directories("${LUAJIT_INCLUDE}") if(NOT WIN32 AND NOT APPLE) include_directories("${B64_INCLUDE}") include_directories("${CURL_INCLUDE_DIR}") include_directories("${CURSES_INCLUDE_DIR}") include_directories("${JQ_INCLUDE}") include_directories("${OPENSSL_INCLUDE_DIR}") endif() add_library(sinsp STATIC chisel.cpp chisel_api.cpp container.cpp ctext.cpp cyclewriter.cpp cursescomponents.cpp cursestable.cpp cursesspectro.cpp cursesui.cpp event.cpp eventformatter.cpp docker.cpp dumper.cpp fdinfo.cpp filter.cpp filterchecks.cpp http_parser.c http_reason.cpp ifinfo.cpp json_query.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 sinsp.cpp stats.cpp table.cpp sinsp_auth.cpp sinsp_curl.cpp stopwatch.cpp uri_parser.c uri.cpp utils.cpp user_event.cpp value_parser.cpp viewinfo.cpp) target_link_libraries(sinsp scap "${JSONCPP_LIB}") if(NOT WIN32) if(USE_BUNDLED_LUAJIT) add_dependencies(sinsp luajit) endif() if(USE_BUNDLED_OPENSSL) add_dependencies(sinsp openssl) endif() if(USE_BUNDLED_CURL) add_dependencies(sinsp curl) endif() if(NOT APPLE) target_link_libraries(sinsp "${JQ_LIB}" "${B64_LIB}" "${CURL_LIBRARIES}" 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.19.1/userspace/libsinsp/chisel.cpp000066400000000000000000001064121316537151600207100ustar00rootroot00000000000000/* 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 #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); 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.19.1/userspace/libsinsp/chisel.h000066400000000000000000000100051316537151600203450ustar00rootroot00000000000000/* 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 . */ #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; char m_dir[1024]; }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[16384]; 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.19.1/userspace/libsinsp/chisel_api.cpp000066400000000000000000000774751316537151600215610ustar00rootroot00000000000000/* 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 #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, const filtercheck_field_info* finfo, uint32_t len) { ASSERT(rawval != NULL); ASSERT(finfo != NULL); switch(finfo->m_type) { 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_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; } default: ASSERT(false); string err = "wrong event type " + to_string((long long) finfo->m_type); 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(), 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) { threadinfo_map_iterator_t it; unordered_map::iterator fdit; uint32_t j; sinsp_filter_compiler* compiler = NULL; sinsp_filter* filter = NULL; sinsp_evt tevt; scap_evt tscapevt; char ipbuf[128]; // // 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; 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); for(it = threadtable->begin(); it != threadtable->end(); ++it) { // // Check if there's at least an fd that matches the filter. // If not, skip this thread // sinsp_fdtable* fdtable = it->second.get_fd_table(); if(filter != NULL) { bool match = false; for(fdit = fdtable->m_table.begin(); fdit != fdtable->m_table.end(); ++fdit) { tevt.m_tinfo = &(it->second); tevt.m_fdinfo = &(fdit->second); tscapevt.tid = it->first; 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) { continue; } } // // Set the thread properties // lua_newtable(ls); lua_pushliteral(ls, "tid"); lua_pushnumber(ls, (uint32_t)it->second.m_tid); lua_settable(ls, -3); lua_pushliteral(ls, "pid"); lua_pushnumber(ls, (uint32_t)it->second.m_pid); lua_settable(ls, -3); if(!barebone) { lua_pushliteral(ls, "ptid"); lua_pushnumber(ls, (uint32_t)it->second.m_ptid); lua_settable(ls, -3); lua_pushliteral(ls, "comm"); lua_pushstring(ls, it->second.m_comm.c_str()); lua_settable(ls, -3); lua_pushliteral(ls, "exe"); lua_pushstring(ls, it->second.m_exe.c_str()); lua_settable(ls, -3); lua_pushliteral(ls, "flags"); lua_pushnumber(ls, (uint32_t)it->second.m_flags); lua_settable(ls, -3); lua_pushliteral(ls, "fdlimit"); lua_pushnumber(ls, (uint32_t)it->second.m_fdlimit); lua_settable(ls, -3); lua_pushliteral(ls, "uid"); lua_pushnumber(ls, (uint32_t)it->second.m_uid); lua_settable(ls, -3); lua_pushliteral(ls, "gid"); lua_pushnumber(ls, (uint32_t)it->second.m_gid); lua_settable(ls, -3); lua_pushliteral(ls, "nchilds"); lua_pushnumber(ls, (uint32_t)it->second.m_nchilds); lua_settable(ls, -3); lua_pushliteral(ls, "vmsize_kb"); lua_pushnumber(ls, (uint32_t)it->second.m_vmsize_kb); lua_settable(ls, -3); lua_pushliteral(ls, "vmrss_kb"); lua_pushnumber(ls, (uint32_t)it->second.m_vmrss_kb); lua_settable(ls, -3); lua_pushliteral(ls, "vmswap_kb"); lua_pushnumber(ls, (uint32_t)it->second.m_vmswap_kb); lua_settable(ls, -3); lua_pushliteral(ls, "pfmajor"); lua_pushnumber(ls, (uint32_t)it->second.m_pfmajor); lua_settable(ls, -3); lua_pushliteral(ls, "pfminor"); lua_pushnumber(ls, (uint32_t)it->second.m_pfminor); lua_settable(ls, -3); lua_pushliteral(ls, "clone_ts"); lua_pushstring(ls, to_string((long long int)it->second.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(it->second.m_uid == 0xffffffff) { username = ""; } else { uit = userlist->find(it->second.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 = &(it->second.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 = it->second.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 = &(it->second); tevt.m_fdinfo = &(fdit->second); tscapevt.tid = it->first; 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) { uint8_t* pip4; if(evt_type == SCAP_FD_IPV4_SOCK) { // cip pip4 = (uint8_t*)&(fdit->second.m_sockinfo.m_ipv4info.m_fields.m_sip); snprintf(ipbuf, sizeof(ipbuf), "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8, pip4[0], pip4[1], pip4[2], pip4[3]); lua_pushliteral(ls, "cip"); lua_pushstring(ls, ipbuf); lua_settable(ls, -3); // sip pip4 = (uint8_t*)&(fdit->second.m_sockinfo.m_ipv4info.m_fields.m_dip); snprintf(ipbuf, sizeof(ipbuf), "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8, pip4[0], pip4[1], pip4[2], pip4[3]); lua_pushliteral(ls, "sip"); lua_pushstring(ls, ipbuf); lua_settable(ls, -3); // cport lua_pushliteral(ls, "cport"); lua_pushnumber(ls, fdit->second.m_sockinfo.m_ipv4info.m_fields.m_sport); lua_settable(ls, -3); // sport lua_pushliteral(ls, "sport"); lua_pushnumber(ls, fdit->second.m_sockinfo.m_ipv4info.m_fields.m_dport); lua_settable(ls, -3); // is_server lua_pushliteral(ls, "is_server"); lua_pushboolean(ls, fdit->second.is_role_server()); lua_settable(ls, -3); } else { // sip pip4 = (uint8_t*)&(fdit->second.m_sockinfo.m_ipv4serverinfo.m_ip); snprintf(ipbuf, sizeof(ipbuf), "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8, pip4[0], pip4[1], pip4[2], pip4[3]); lua_pushliteral(ls, "sip"); lua_pushstring(ls, ipbuf); lua_settable(ls, -3); // sport lua_pushliteral(ls, "sport"); lua_pushnumber(ls, fdit->second.m_sockinfo.m_ipv4serverinfo.m_port); lua_settable(ls, -3); // is_server lua_pushliteral(ls, "is_server"); lua_pushboolean(ls, 1); 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)it->first); } 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 { 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.19.1/userspace/libsinsp/chisel_api.h000066400000000000000000000051721316537151600212070ustar00rootroot00000000000000/* 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 . */ #pragma once #ifdef HAS_CHISELS class lua_cbacks { public: static uint32_t rawval_to_lua_stack(lua_State *ls, uint8_t* rawval, const filtercheck_field_info* finfo, 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.19.1/userspace/libsinsp/container.cpp000066400000000000000000000636071316537151600214330ustar00rootroot00000000000000/* 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 . */ #ifndef _WIN32 #include #include #include #endif #include "sinsp.h" #include "sinsp_int.h" #include "container.h" #include "utils.h" void sinsp_container_info::parse_json_mounts(const Json::Value &mnt_obj, vector &mounts) { if(!mnt_obj.isNull() && mnt_obj.isArray()) { for(uint32_t i=0; i= m_mounts.size()) { return NULL; } return &(m_mounts[idx]); } sinsp_container_info::container_mount_info *sinsp_container_info::mount_by_source(std::string &source) { // 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; } sinsp_container_info::container_mount_info *sinsp_container_info::mount_by_dest(std::string &dest) { // 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; } sinsp_container_manager::sinsp_container_manager(sinsp* inspector) : m_inspector(inspector), m_last_flush_time_ns(0) { } 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_thread_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(); for(threadinfo_map_iterator_t it = threadtable->begin(); it != threadtable->end(); ++it) { if(!it->second.m_container_id.empty()) { containers_in_use.insert(it->second.m_container_id); } } for(unordered_map::iterator it = m_containers.begin(); it != m_containers.end();) { if(containers_in_use.find(it->first) == containers_in_use.end()) { if(m_inspector->m_parser->m_fd_listener) { m_inspector->m_parser->m_fd_listener->on_remove_container(m_containers[it->first]); } m_containers.erase(it++); } else { ++it; } } } return res; } bool sinsp_container_manager::get_container(const string& container_id, sinsp_container_info* container_info) const { unordered_map::const_iterator it = m_containers.find(container_id); if(it != m_containers.end()) { *container_info = it->second; return true; } return false; } sinsp_container_info* sinsp_container_manager::get_container(const string& container_id) { unordered_map::iterator it = m_containers.find(container_id); if(it != m_containers.end()) { return &it->second; } return NULL; } string sinsp_container_manager::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 sinsp_container_manager::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); if(!mtid.empty()) { g_logger.log("Mesos native container: [" + container->m_id + "], Mesos task ID: " + mtid, sinsp_logger::SEV_DEBUG); return true; } else { g_logger.log("Mesos task ID not found for Mesos container [" + container->m_id + "]," "thread [" + std::to_string(tinfo->m_tid) + ']', sinsp_logger::SEV_DEBUG); } } } return false; } string sinsp_container_manager::get_mesos_task_id(const string& container_id) { string mesos_task_id; const sinsp_container_info* container = get_container(container_id); if(container) { mesos_task_id = container->m_mesos_task_id; } return mesos_task_id; } bool sinsp_container_manager::resolve_container(sinsp_threadinfo* tinfo, bool query_os_for_missing_info) { ASSERT(tinfo); bool valid_id = false; sinsp_container_info container_info; string rkt_podid, rkt_appname; // Start with cgroup based detection for(auto it = tinfo->m_cgroups.begin(); it != tinfo->m_cgroups.end(); ++it) { string cgroup = it->second; size_t pos; // // Non-systemd Docker // pos = cgroup.find_last_of("/"); if(pos != string::npos) { if(cgroup.length() - pos - 1 == 64 && cgroup.find_first_not_of("0123456789abcdefABCDEF", pos + 1) == string::npos) { container_info.m_type = CT_DOCKER; container_info.m_id = cgroup.substr(pos + 1, 12); valid_id = true; break; } } // // systemd Docker // pos = cgroup.find("docker-"); if(pos != string::npos) { size_t pos2 = cgroup.find(".scope"); if(pos2 != string::npos && pos2 - pos - sizeof("docker-") + 1 == 64) { container_info.m_type = CT_DOCKER; container_info.m_id = cgroup.substr(pos + sizeof("docker-") - 1, 12); valid_id = true; break; } } // // Non-systemd libvirt-lxc // pos = cgroup.find(".libvirt-lxc"); if(pos != string::npos && pos == cgroup.length() - sizeof(".libvirt-lxc") + 1) { size_t pos2 = cgroup.find_last_of("/"); if(pos2 != string::npos) { container_info.m_type = CT_LIBVIRT_LXC; container_info.m_id = cgroup.substr(pos2 + 1, pos - pos2 - 1); valid_id = true; break; } } // // systemd libvirt-lxc // pos = cgroup.find("-lxc\\x2"); if(pos != string::npos) { size_t pos2 = cgroup.find(".scope"); if(pos2 != 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")); valid_id = true; break; } } // // Legacy libvirt-lxc // pos = cgroup.find("/libvirt/lxc/"); if(pos != string::npos) { container_info.m_type = CT_LIBVIRT_LXC; container_info.m_id = cgroup.substr(pos + sizeof("/libvirt/lxc/") - 1); valid_id = true; break; } // // Non-systemd LXC // pos = cgroup.find("/lxc/"); if(pos != 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); valid_id = true; break; } // // Mesos // pos = cgroup.find("/mesos/"); if(pos != string::npos) { container_info.m_type = CT_MESOS; container_info.m_id = cgroup.substr(pos + sizeof("/mesos/") - 1); // 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 valid_id = set_mesos_task_id(&container_info, tinfo); break; } // // systemd rkt // // rkt cgroups // 1. /system.slice/k8s_d1efb75a-ad42-458e-af65-2b378f42173f.service/system.slice/redis.service // 2. /machine.slice/machine-rkt\x2dc508ad4c\x2d7fa4\x2d4513\x2d9d53\x2d007628003805.scope/system.slice/redis.service 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 = [&rkt_podid](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 = m_containers.find(rkt_podid + ":" + rkt_appname) != m_containers.end(); // 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; valid_id = true; break; } } } } // If anything has been found, try proc root based detection // right now used for rkt if(!valid_id) { // 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) { 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 = [&rkt_podid, &container_info, &rkt_appname, &valid_id] (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); } } } 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; valid_id = true; } } } } } if(!valid_id) { tinfo->m_container_id = ""; } else { tinfo->m_container_id = container_info.m_id; unordered_map::const_iterator it = m_containers.find(container_info.m_id); if(it == m_containers.end()) { switch(container_info.m_type) { case CT_DOCKER: #ifndef _WIN32 if(query_os_for_missing_info) { parse_docker(&container_info); } #endif break; case CT_LXC: container_info.m_name = container_info.m_id; break; case CT_LIBVIRT_LXC: container_info.m_name = container_info.m_id; break; case CT_MESOS: container_info.m_name = container_info.m_id; break; case CT_RKT: #ifndef _WIN32 if(query_os_for_missing_info) { parse_rkt(&container_info, rkt_podid, rkt_appname); } #endif break; default: ASSERT(false); } add_container(container_info); if(container_to_sinsp_event(container_to_json(container_info), &m_inspector->m_meta_evt)) { m_inspector->m_meta_evt_pending = true; } } } return valid_id; } 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["privileged"] = container_info.m_privileged; 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; char addrbuff[100]; uint32_t iph = htonl(container_info.m_container_ip); inet_ntop(AF_INET, &iph, addrbuff, sizeof(addrbuff)); container["ip"] = addrbuff; if(!container_info.m_mesos_task_id.empty()) { container["mesos_task_id"] = container_info.m_mesos_task_id; } return Json::FastWriter().write(obj); } bool sinsp_container_manager::container_to_sinsp_event(const string& json, sinsp_evt* evt) { // TODO: variable event length size_t evt_len = SP_EVT_BUF_SIZE; size_t totlen = sizeof(scap_evt) + sizeof(uint16_t) + json.length() + 1; if(totlen > evt_len) { ASSERT(false); return false; } evt->m_cpuid = 0; evt->m_evtnum = 0; evt->m_inspector = m_inspector; scap_evt* scapevt = evt->m_pevt; scapevt->ts = m_inspector->m_lastevent_ts; scapevt->tid = 0; scapevt->len = (uint32_t)totlen; scapevt->type = PPME_CONTAINER_JSON_E; 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(); return true; } #ifndef _WIN32 bool sinsp_container_manager::parse_docker(sinsp_container_info* container) { string file = string(scap_get_host_root()) + "/var/run/docker.sock"; int sock = socket(PF_UNIX, SOCK_STREAM, 0); if(sock < 0) { ASSERT(false); return false; } struct sockaddr_un address; memset(&address, 0, sizeof(struct sockaddr_un)); address.sun_family = AF_UNIX; strncpy(address.sun_path, file.c_str(), sizeof(address.sun_path) - 1); address.sun_path[sizeof(address.sun_path) - 1]= '\0'; if(connect(sock, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0) { return false; } string message = "GET /containers/" + container->m_id + "/json HTTP/1.0\r\n\n"; if(write(sock, message.c_str(), message.length()) != (ssize_t) message.length()) { ASSERT(false); close(sock); return false; } char buf[256]; string json; ssize_t res; while((res = read(sock, buf, sizeof(buf) - 1)) != 0) { if(res == -1 || json.size() > MAX_JSON_SIZE_B) { ASSERT(false); close(sock); return false; } buf[res] = 0; json += buf; } close(sock); size_t pos = json.find("{"); if(pos == string::npos) { ASSERT(false); return false; } Json::Value root; Json::Reader reader; bool parsingSuccessful = reader.parse(json.substr(pos), root); if(!parsingSuccessful) { 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); } container->m_name = root["Name"].asString(); if(!container->m_name.empty() && container->m_name[0] == '/') { container->m_name = container->m_name.substr(1); } 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) { sinsp_container_info pcnt; pcnt.m_id = net_mode.substr(net_mode.find(":") + 1); if(!get_container(pcnt.m_id, &pcnt)) { parse_docker(&pcnt); } container->m_container_ip = pcnt.m_container_ip; } } 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"]; string mesos_task_id = get_docker_env(env_vars, "MESOS_TASK_ID"); if(mesos_task_id.empty()) { mesos_task_id = get_docker_env(env_vars, "mesos_task_id"); } if(mesos_task_id.empty()) { mesos_task_id = get_docker_env(env_vars, "MESOS_EXECUTOR_ID"); } if(!mesos_task_id.empty()) { container->m_mesos_task_id = mesos_task_id; g_logger.log("Mesos Docker container: [" + root["Id"].asString() + "], Mesos task ID: [" + container->m_mesos_task_id + ']', sinsp_logger::SEV_DEBUG); } 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 Json::Value &privileged = host_config_obj["Privileged"]; if(!privileged.isNull() && privileged.isBool()) { container->m_privileged = privileged.asBool(); } sinsp_container_info::parse_json_mounts(root["Mounts"], container->m_mounts); #ifdef HAS_ANALYZER container->m_sysdig_agent_conf = get_docker_env(env_vars, "SYSDIG_AGENT_CONF"); #endif return true; } string sinsp_container_manager::get_docker_env(const Json::Value &env_vars, const string &mti) { string ret; for(const auto& env_var : env_vars) { if(env_var.isString()) { ret = env_var.asString(); if((ret.length() > (mti.length() + 1)) && (ret.substr(0, mti.length()) == mti)) { return ret.substr(mti.length() + 1); } } } return ""; } bool sinsp_container_manager::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; } #endif const unordered_map* sinsp_container_manager::get_containers() { return &m_containers; } void sinsp_container_manager::add_container(const sinsp_container_info& container_info) { m_containers[container_info.m_id] = container_info; if(m_inspector->m_parser->m_fd_listener) { m_inspector->m_parser->m_fd_listener->on_new_container(container_info); } } void sinsp_container_manager::dump_containers(scap_dumper_t* dumper) { for(unordered_map::const_iterator it = m_containers.begin(); it != m_containers.end(); ++it) { if(container_to_sinsp_event(container_to_json(it->second), &m_inspector->m_meta_evt)) { int32_t res = scap_dump(m_inspector->m_h, dumper, m_inspector->m_meta_evt.m_pevt, m_inspector->m_meta_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 { sinsp_container_info container_info; bool found = get_container(tinfo->m_container_id, &container_info); if(!found) { return NULL; } if(container_info.m_name.empty()) { return NULL; } res = container_info.m_name; } return res; } sysdig-0.19.1/userspace/libsinsp/container.h000066400000000000000000000101011316537151600210550ustar00rootroot00000000000000/* 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 . */ #pragma once enum sinsp_container_type { CT_DOCKER = 0, CT_LXC = 1, CT_LIBVIRT_LXC = 2, CT_MESOS = 3, CT_RKT = 4 }; 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 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() { 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; }; 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) { } static void parse_json_mounts(const Json::Value &mnt_obj, vector &mounts); container_mount_info *mount_by_idx(uint32_t idx); container_mount_info *mount_by_source(std::string &source); container_mount_info *mount_by_dest(std::string &dest); string m_id; sinsp_container_type m_type; string m_name; string m_image; string m_imageid; uint32_t m_container_ip; bool m_privileged; vector m_mounts; vector m_port_mappings; map m_labels; 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; #ifdef HAS_ANALYZER string m_sysdig_agent_conf; #endif }; class sinsp_container_manager { public: sinsp_container_manager(sinsp* inspector); const unordered_map* get_containers(); bool remove_inactive_containers(); void add_container(const sinsp_container_info& container_info); bool get_container(const string& id, sinsp_container_info* container_info) const; 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); string get_env_mesos_task_id(sinsp_threadinfo* tinfo); bool set_mesos_task_id(sinsp_container_info* container, sinsp_threadinfo* tinfo); string get_mesos_task_id(const string& container_id); private: string container_to_json(const sinsp_container_info& container_info); bool container_to_sinsp_event(const string& json, sinsp_evt* evt); bool parse_docker(sinsp_container_info* container); string get_docker_env(const Json::Value &env_vars, const string &mti); bool parse_rkt(sinsp_container_info* container, const string& podid, const string& appname); sinsp_container_info* get_container(const string& id); sinsp* m_inspector; unordered_map m_containers; uint64_t m_last_flush_time_ns; }; sysdig-0.19.1/userspace/libsinsp/ctext.cpp000066400000000000000000000744751316537151600206050ustar00rootroot00000000000000/* Copyright (C) 2013-2015 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 . */ #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 exsting 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.19.1/userspace/libsinsp/ctext.h000066400000000000000000000343361316537151600202420ustar00rootroot00000000000000/* Copyright (C) 2013-2015 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 . */ #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 aviailable 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 optiosn 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.19.1/userspace/libsinsp/cursescomponents.cpp000066400000000000000000001266151316537151600230620ustar00rootroot00000000000000/* Copyright (C) 2013-2015 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 #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.19.1/userspace/libsinsp/cursescomponents.h000066400000000000000000000127231316537151600225210ustar00rootroot00000000000000/* 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 . */ 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() { } // // Retuens 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.19.1/userspace/libsinsp/cursesspectro.cpp000066400000000000000000000330011316537151600223360ustar00rootroot00000000000000/* 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 #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.19.1/userspace/libsinsp/cursesspectro.h000066400000000000000000000060361316537151600220130ustar00rootroot00000000000000/* 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 . */ #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 // CSYSDIGsysdig-0.19.1/userspace/libsinsp/cursestable.cpp000066400000000000000000000435641316537151600217650ustar00rootroot00000000000000/* 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 #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_DYN] = 8; m_colsizes[PT_FLAGS8] = 32; m_colsizes[PT_FLAGS16] = 32; m_colsizes[PT_FLAGS32] = 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.19.1/userspace/libsinsp/cursestable.h000066400000000000000000000046001316537151600214160ustar00rootroot00000000000000/* 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 . */ #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.19.1/userspace/libsinsp/cursesui.cpp000066400000000000000000002103441316537151600213030ustar00rootroot00000000000000/* Copyright (C) 2013-2015 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 "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++; 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(); if(fdname != "") { sanitize_string(fdname); line["f"] = to_string(fdnum) + "(<" + string(tc) + ">" + 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()) { sinsp_container_info container_info; bool found = m_inspector->m_container_manager.get_container(tinfo->m_container_id, &container_info); if(found) { 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.19.1/userspace/libsinsp/cursesui.h000066400000000000000000000417411316537151600207530ustar00rootroot00000000000000/* 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 . */ #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.19.1/userspace/libsinsp/cyclewriter.cpp000066400000000000000000000120471316537151600217750ustar00rootroot00000000000000#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.19.1/userspace/libsinsp/cyclewriter.h000066400000000000000000000066031316537151600214430ustar00rootroot00000000000000#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.19.1/userspace/libsinsp/docker.cpp000066400000000000000000000325141316537151600207110ustar00rootroot00000000000000// // docker.cpp // #if defined(__linux__) #include "sinsp.h" #include "sinsp_int.h" #include "docker.h" #include "user_event.h" const std::string docker::DOCKER_SOCKET_FILE = "/var/run/docker.sock"; docker::docker(std::string url, const std::string& path, const std::string& http_version, int timeout_ms, bool is_captured, bool verbose, event_filter_ptr_t event_filter): m_id("docker"), m_timeout_ms(timeout_ms), m_is_captured(is_captured), m_verbose(verbose), m_event_filter(event_filter), m_container_events{"attach", "commit", "copy", "create", "destroy", "die", "exec_create", "exec_start", "export", "kill", "oom", "pause", "rename", "resize", "restart", "start", "stop", "top", "unpause", "update"}, m_image_events{"delete", "import", "pull", "push", "tag", "untag"}, m_volume_events{"create", "mount", "unmount", "destroy"}, m_network_events{"create", "connect", "disconnect", "destroy"}, m_severity_map { // container { "attach", sinsp_logger::SEV_EVT_INFORMATION }, { "commit", sinsp_logger::SEV_EVT_INFORMATION }, { "copy", sinsp_logger::SEV_EVT_INFORMATION }, { "create", sinsp_logger::SEV_EVT_INFORMATION }, { "destroy", sinsp_logger::SEV_EVT_WARNING }, { "die", sinsp_logger::SEV_EVT_WARNING }, { "exec_create", sinsp_logger::SEV_EVT_INFORMATION }, { "exec_start", sinsp_logger::SEV_EVT_INFORMATION }, { "export", sinsp_logger::SEV_EVT_INFORMATION }, { "kill", sinsp_logger::SEV_EVT_WARNING }, { "oom", sinsp_logger::SEV_EVT_WARNING }, { "pause", sinsp_logger::SEV_EVT_INFORMATION }, { "rename", sinsp_logger::SEV_EVT_INFORMATION }, { "resize", sinsp_logger::SEV_EVT_INFORMATION }, { "restart", sinsp_logger::SEV_EVT_WARNING }, { "start", sinsp_logger::SEV_EVT_INFORMATION }, { "stop", sinsp_logger::SEV_EVT_INFORMATION }, { "top", sinsp_logger::SEV_EVT_INFORMATION }, { "unpause", sinsp_logger::SEV_EVT_INFORMATION }, { "update", sinsp_logger::SEV_EVT_INFORMATION }, // image { "delete", sinsp_logger::SEV_EVT_INFORMATION }, { "import", sinsp_logger::SEV_EVT_INFORMATION }, { "pull", sinsp_logger::SEV_EVT_INFORMATION }, { "push", sinsp_logger::SEV_EVT_INFORMATION }, { "tag", sinsp_logger::SEV_EVT_INFORMATION }, { "untag", sinsp_logger::SEV_EVT_INFORMATION }, // volume { "mount", sinsp_logger::SEV_EVT_INFORMATION }, { "unmount", sinsp_logger::SEV_EVT_INFORMATION }, // network { "connect", sinsp_logger::SEV_EVT_INFORMATION }, { "disconnect", sinsp_logger::SEV_EVT_INFORMATION } }, m_name_translation { // Container { "attach", "Attached" }, { "commit", "Committed" }, { "copy", "Copied" }, { "create", "Created" }, { "destroy", "Destroyed" }, { "die", "Died" }, { "exec_create", "Exec Created" }, { "exec_start", "Exec Started" }, { "export", "Exported" }, { "kill", "Killed" }, { "oom", "Out of Memory" }, { "pause", "Paused" }, { "rename", "Renamed" }, { "resize", "Resized" }, { "restart", "Restarted" }, { "start", "Started" }, { "stop", "Stopped" }, { "top", "Top" }, { "unpause", "Unpaused" }, { "update", "Updated" }, // Image { "delete", "Deleted" }, { "import", "Imported" }, { "pull", "Pulled" }, { "push", "Pushed" }, { "tag", "Tagged" }, { "untag", "Untagged" }, // Volume // { "create", "Created" }, duplicate { "mount", "Mounted" }, { "unmount", "Unmounted" }, // { "destroy", "Destroyed" }, duplicate // Network // { "create", "Created" }, duplicate { "connect", "Connected" }, { "disconnect", "Disconnected" } // { "destroy" "Destroyed" } duplicate } { g_logger.log(std::string("Creating Docker object for " + (url.empty() ? std::string("capture replay") : url)), sinsp_logger::SEV_DEBUG); #ifdef HAS_CAPTURE if(url.empty()) { url = std::string("file://").append(scap_get_host_root()).append(DOCKER_SOCKET_FILE); } m_event_http = std::make_shared(*this, "docker", url, path, http_version, timeout_ms, nullptr, nullptr, true, false, 524288u, false); m_event_http->set_json_callback(&docker::set_event_json); m_event_http->add_json_filter("."); m_collector.add(m_event_http); m_collector.set_steady_state(true); send_data_request(); #endif } docker::~docker() { } #ifdef HAS_CAPTURE void docker::send_event_data_request() { if(m_event_http) { m_event_http->send_request(); } else { throw sinsp_exception("Docker event HTTP client is null."); } } void docker::connect() { if(!connect(m_event_http, &docker::set_event_json, 1)) { throw sinsp_exception("Connection to Docker API failed."); } } #endif // HAS_CAPTURE bool docker::is_alive() const { #ifdef HAS_CAPTURE if(m_event_http && !m_event_http->is_connected()) { g_logger.log("Docker state connection loss.", sinsp_logger::SEV_WARNING); return false; } #endif // HAS_CAPTURE return true; } #ifdef HAS_CAPTURE void docker::check_collector_status() { if(!m_collector.is_healthy(m_event_http)) { throw sinsp_exception("Docker collector not healthy, " "giving up on data collection in this cycle ..."); } } void docker::send_data_request(bool collect) { if(m_events.size()) { return; } connect(); send_event_data_request(); g_logger.log("Docker event request sent.", sinsp_logger::SEV_DEBUG); if(collect) { collect_data(); } } void docker::collect_data() { if(m_collector.subscription_count()) { if(!m_event_http->is_enabled()) { m_event_http->enable(); } m_collector.get_data(); if(m_events.size()) { for(auto evt : m_events) { if(evt && !evt->isNull()) { handle_event(std::move(*evt)); } else { g_logger.log(std::string("Docker event error: ") + (!evt ? "event is null." : (evt->isNull() ? "JSON is null." : "Unknown")), sinsp_logger::SEV_ERROR); } } m_events.clear(); } } } #endif // HAS_CAPTURE void docker::set_event_json(json_ptr_t json, const std::string&) { if(m_event_filter) { m_events.emplace_back(json); } } #ifdef HAS_CAPTURE void docker::emit_event(Json::Value& root, std::string type, std::string status, bool send_to_backend) { ++m_event_counter; std::string::size_type delim_pos = status.find(':'); if(delim_pos != std::string::npos) { status = status.substr(0, delim_pos); } g_logger.log("Docker EVENT: handling " + status + " of " + type, sinsp_logger::SEV_DEBUG); severity_map_t::const_iterator it = m_severity_map.find(status); severity_t severity; std::string event_name = status; std::string id = get_json_string(root, "id"); if(id.length() > 7 && id.substr(0, 7) == "sha256:") // untag and delete have "sha256:id" format { id.clear(); // ignore that (will be displayed in event description) } severity = it->second; g_logger.log("Docker EVENT: severity for " + status + '=' + std::to_string(severity - sinsp_logger::SEV_EVT_MIN), sinsp_logger::SEV_DEBUG); uint64_t epoch_time_s = static_cast(~0); Json::Value t = root["time"]; if(!t.isNull() && t.isConvertibleTo(Json::uintValue)) { epoch_time_s = t.asUInt64(); } g_logger.log("Docker EVENT: name=" + event_name + ", id=" + id + ", status=" + status + ", time=" + std::to_string(epoch_time_s), sinsp_logger::SEV_DEBUG); if(m_verbose) { std::cout << Json::FastWriter().write(root) << std::endl; } Json::Value no_value = Json::nullValue; const Json::Value& actor = root["Actor"]; const Json::Value& attrib = actor.isNull() ? no_value : actor["Attributes"]; const Json::Value& img = attrib.isNull() ? no_value : attrib["image"]; std::string image; if(!img.isNull() && img.isConvertibleTo(Json::stringValue)) { image = img.asString(); } event_scope scope; if(m_machine_id.length()) { scope.add("host.mac", m_machine_id); } if(is_image_event(event_name)) { bool id_was_empty = false; if(id.empty()) { id = get_json_string(root, "id"); id_was_empty = true; } if(!id.empty()) { scope.add("container.image", id); } else if(!image.empty()) { scope.add("container.image", image); } else { g_logger.log("Cannot determine container image for Docker event.", sinsp_logger::SEV_WARNING); } if(id_was_empty) { id.clear(); } } else if(is_container_event(event_name)) { if(id.length() >= 12) { scope.add("container.id", id.substr(0, 12)); } } if(status.length()) { status.insert(0, "Event: ", 7); } if(!actor.isNull() && actor.isObject()) { if(!attrib.isNull() && attrib.isObject()) { if(!image.empty()) { status.append("; Image: ").append(image); } if(!id.empty() && id != image) { status.append("; ID: ").append(id); } const Json::Value& name = attrib["name"]; if(!name.isNull() && name.isConvertibleTo(Json::stringValue)) { status.append("; Name: ").append(name.asString()); } } } sinsp_user_event::tag_map_t tags; tags["source"] = "docker"; if(event_name.length()) { if(type.length()) { type[0] = toupper(type[0]); event_name = type.append(1, ' ').append(translate_name(event_name)); } else // older docker versions don't tell type { event_name[0] = toupper(event_name[0]); event_name.insert(0, "Docker "); } } std::string evt = sinsp_user_event::to_string(epoch_time_s, std::move(event_name), std::move(status), std::move(scope), std::move(tags)); if(send_to_backend) { if(it != m_severity_map.end()) { // // This is where the event is sent to the backend // g_logger.log(evt, severity); if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log("Docker EVENT: scheduled for sending\n" + evt, sinsp_logger::SEV_TRACE); } } else { g_logger.log("Docker EVENT: status not supported: " + status, sinsp_logger::SEV_ERROR); if(g_logger.get_severity() >= sinsp_logger::SEV_DEBUG) { g_logger.log(Json::FastWriter().write(root), sinsp_logger::SEV_DEBUG); } } } // // This is where the event is sent to the memdumper // g_logger.log(std::move(evt), (severity_t)sinsp_logger::SEV_EVT_MDUMP); } void docker::handle_event(Json::Value&& root) { if(m_event_filter && (m_event_counter < sinsp_user_event::max_events_per_cycle())) { std::string type = get_json_string(root, "Type"); std::string status = get_json_string(root, "Action"); if(status.empty()) { status = get_json_string(root, "status"); } g_logger.log("Docker EVENT: type=" + type + ", status=" + status + ", " "queued events count=" + std::to_string(m_event_counter), sinsp_logger::SEV_DEBUG); bool is_allowed = m_event_filter->allows_all(); if(!is_allowed) { if(!type.empty()) { is_allowed = m_event_filter->allows_all(type); if(!is_allowed && !status.empty()) { is_allowed = m_event_filter->has(type, status); } // status for exec_* events is different, eg.: // "container:exec_create: ls -l" if(!is_allowed) { std::string exec_create = "exec_create"; std::string exec_start = "exec_start"; std::string::size_type pos = status.find(exec_create); if(pos != std::string::npos) { status = exec_create; g_logger.log("Docker EVENT: found exec_create status=" + status, sinsp_logger::SEV_TRACE); } else { pos = status.find(exec_start); if(pos != std::string::npos) { status = exec_start; g_logger.log("Docker EVENT: found exec_start status=" + status, sinsp_logger::SEV_TRACE); } } if(pos != std::string::npos) { is_allowed = m_event_filter->has(type, status); g_logger.log("Docker EVENT: status=" + status + (is_allowed ? " is " : " is not ") + "allowed", sinsp_logger::SEV_TRACE); } } } else // older docker versions don't tell type, so there will be some overlap of duplicates { is_allowed = m_event_filter->has("container", status); if(!is_allowed && !status.empty()) { is_allowed = m_event_filter->has("image", status); } if(!is_allowed && !status.empty()) { is_allowed = m_event_filter->has("volume", status); } if(!is_allowed && !status.empty()) { is_allowed = m_event_filter->has("volume", status); } } } if(is_allowed) { emit_event(root, type, status, true); } else { emit_event(root, type, status, false); if(g_logger.get_severity() >= sinsp_logger::SEV_TRACE) { g_logger.log("Docker EVENT: status not permitted by filter: " + type +':' + status, sinsp_logger::SEV_TRACE); g_logger.log(Json::FastWriter().write(root), sinsp_logger::SEV_TRACE); } } m_event_limit_exceeded = false; } else if(!m_event_limit_exceeded) // only get in here once per cycle, to send event overflow warning { sinsp_user_event::emit_event_overflow("Docker", get_machine_id()); m_event_limit_exceeded = true; } } std::string docker::get_socket_file() { string sock_file = scap_get_host_root(); std::string::size_type len = sock_file.length(); if(len && sock_file[len - 1] == '/') { if((len - 1) > 0) { sock_file = sock_file.substr(0, len - 1); } else { sock_file.clear(); } } sock_file.append(DOCKER_SOCKET_FILE); return sock_file; } #endif // HAS_CAPTURE #endif // __linux__ sysdig-0.19.1/userspace/libsinsp/docker.h000066400000000000000000000111321316537151600203470ustar00rootroot00000000000000// // docker.h // #if defined(__linux__) #pragma once #include "json/json.h" #include "socket_collector.h" #include "uri.h" #include "user_event.h" #include #include #include class docker { public: typedef std::vector uri_list_t; typedef std::shared_ptr json_ptr_t; typedef std::set event_filter_t; typedef user_event_filter_t::ptr_t event_filter_ptr_t; static const int default_timeout_ms = 1000L; docker(std::string url = "", const std::string& path = "/events", const std::string& http_version = "1.0", int timeout_ms = default_timeout_ms, bool is_captured = false, bool verbose = false, event_filter_ptr_t event_filter = nullptr); ~docker(); bool is_alive() const; void set_event_json(json_ptr_t json, const std::string&); void simulate_event(const std::string& json); const std::string& get_id() const; void reset_event_counter(); #ifdef HAS_CAPTURE void send_data_request(bool collect = true); void collect_data(); void set_event_filter(event_filter_ptr_t event_filter); void set_machine_id(const std::string& machine_id); const std::string& get_machine_id() const; static std::string get_socket_file(); private: void connect(); void send_event_data_request(); void check_collector_status(); template bool connect(T http, typename T::element_type::json_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_json_callback(func); m_collector.add(http); } check_collector_status(); return m_collector.has(http); } return false; } bool is_container_event(const std::string& evt_name); bool is_image_event(const std::string& evt_name); bool is_volume_event(const std::string& evt_name); bool is_network_event(const std::string& evt_name); typedef socket_data_handler handler_t; typedef handler_t::ptr_t handler_ptr_t; typedef socket_collector collector_t; handler_ptr_t m_event_http; collector_t m_collector; std::string m_event_uri; #endif // HAS_CAPTURE private: static const std::string DOCKER_SOCKET_FILE; typedef std::vector event_list_t; typedef sinsp_logger::event_severity severity_t; typedef std::unordered_map severity_map_t; typedef std::unordered_map name_translation_map_t; typedef std::set entity_events_t; const std::string& translate_name(const std::string& event_name); void emit_event(Json::Value& root, std::string type, std::string status, bool send_to_backend); void handle_event(Json::Value&& root); std::string m_id; long m_timeout_ms; bool m_is_captured; bool m_verbose; event_filter_ptr_t m_event_filter; std::string m_machine_id; event_list_t m_events; const entity_events_t m_container_events; const entity_events_t m_image_events; const entity_events_t m_volume_events; const entity_events_t m_network_events; severity_map_t m_severity_map; name_translation_map_t m_name_translation; size_t m_event_counter = 0; bool m_event_limit_exceeded = false; }; #ifdef HAS_CAPTURE inline const std::string& docker::get_id() const { return m_id; } inline void docker::set_event_filter(event_filter_ptr_t event_filter) { m_event_filter = event_filter; } inline void docker::set_machine_id(const std::string& machine_id) { m_machine_id = machine_id; } inline const std::string& docker::get_machine_id() const { return m_machine_id; } #endif // HAS_CAPTURE inline const std::string& docker::translate_name(const std::string& event_name) { const auto& it = m_name_translation.find(event_name); if(it != m_name_translation.end()) { return it->second; } return event_name; } #ifdef HAS_CAPTURE inline bool docker::is_container_event(const std::string& evt_name) { return m_container_events.find(evt_name) != m_container_events.end(); } inline bool docker::is_image_event(const std::string& evt_name) { return m_image_events.find(evt_name) != m_image_events.end(); } inline bool docker::is_volume_event(const std::string& evt_name) { return m_volume_events.find(evt_name) != m_volume_events.end(); } inline bool docker::is_network_event(const std::string& evt_name) { return m_network_events.find(evt_name) != m_network_events.end(); } #endif // HAS_CAPTURE inline void docker::reset_event_counter() { m_event_counter = 0; } #endif // __linux__ sysdig-0.19.1/userspace/libsinsp/doxygen/000077500000000000000000000000001316537151600204065ustar00rootroot00000000000000sysdig-0.19.1/userspace/libsinsp/doxygen/conf.dox000066400000000000000000000264561316537151600220640ustar00rootroot00000000000000# 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.19.1/userspace/libsinsp/doxygen/footer.html000066400000000000000000000000301316537151600225630ustar00rootroot00000000000000
sysdig-0.19.1/userspace/libsinsp/doxygen/header.html000066400000000000000000000024221316537151600225240ustar00rootroot00000000000000--- layout: default title: sysdig | libsinsp ---

$projectname $projectnumber

(Generated by Doxygen)

$projectbrief

$projectbrief
$searchbox
sysdig-0.19.1/userspace/libsinsp/dumper.cpp000066400000000000000000000075631316537151600207440ustar00rootroot00000000000000/* 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 "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); } else { m_dumper = scap_dump_open(m_inspector->m_h, filename.c_str(), SCAP_COMPRESSION_NONE); } } 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, true); } else { m_dumper = scap_dump_open_fd(m_inspector->m_h, fd, SCAP_COMPRESSION_NONE, true); } 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.19.1/userspace/libsinsp/dumper.h000066400000000000000000000061131316537151600203770ustar00rootroot00000000000000/* 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 . */ #pragma once class sinsp; class sinsp_evt; /** @defgroup dump Dumping events to disk * Classes to perform miscellneous 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 tracefile 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 tracefile. \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.19.1/userspace/libsinsp/event.cpp000066400000000000000000001465651316537151600205770ustar00rootroot00000000000000/* 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 . */ #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_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_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() { } void sinsp_evt::set_check_id(int32_t id) { if (id) { m_check_id = id; } } int32_t sinsp_evt::get_check_id() { return m_check_id; } 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; } 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-termninate 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 { // // 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 { // // 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) { ASSERT(id < m_info->nparams); 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; } // // 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_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; ASSERT(id < m_info->nparams); // // 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; } // // 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 outpur 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 { 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 = *(uint32_t *)payload & (((uint64_t)1 << payload_len * 8) - 1); 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_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; 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 sysdig-0.19.1/userspace/libsinsp/event.h000066400000000000000000000265201316537151600202300ustar00rootroot00000000000000/* 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 . */ #pragma once #include #ifndef VISIBILITY_PRIVATE #define VISIBILITY_PRIVATE private: #endif typedef class sinsp sinsp; typedef class sinsp_threadinfo sinsp_threadinfo; /////////////////////////////////////////////////////////////////////////////// // 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 desgned 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; ///< Lenght 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: /*! \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 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; } /*! \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. */ string get_param_value_str(const 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); /*! \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(); #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 // 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 = NULL; m_fdinfo = NULL; 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 = NULL; m_fdinfo = NULL; m_iosize = 0; m_cpuid = cpuid; m_evtnum = 0; m_poriginal_evt = NULL; } inline void load_params() { uint32_t j; uint32_t nparams; sinsp_evt_param par; nparams = m_info->nparams; uint16_t *lens = (uint16_t *)((char *)m_pevt + sizeof(struct ppm_evt_hdr)); char *valptr = (char *)lens + 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]; } } string get_param_value_str(uint32_t id, bool resolved); 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) uint16_t m_cpuid; uint64_t m_evtnum; uint32_t m_flags; int32_t m_check_id = 0; bool m_params_loaded; const struct ppm_event_info* m_info; vector m_params; vector m_paramstr_storage; vector m_resolved_paramstr_storage; sinsp_threadinfo* m_tinfo; sinsp_fdinfo_t* m_fdinfo; 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; }; /*@}*/ sysdig-0.19.1/userspace/libsinsp/eventformatter.cpp000066400000000000000000000147141316537151600225110ustar00rootroot00000000000000/* 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 "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.push_back(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); j += chk->parse_field_name(cfmt + j + 1, true, false); ASSERT(j <= lfmt.length()); m_tokens.push_back(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.push_back(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::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]->tojson(evt); if(retval == false) { continue; } if(json_value == Json::nullValue && m_require_all_values) { retval = false; continue; } fi = m_tokens[j]->get_field_info(); if(fi) { m_root[fi->m_name] = m_tokens[j]->tojson(evt); } } else { char* str = m_tokens[j]->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) = "\n"; (*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::tostring(sinsp_evt* evt, OUT string* res) { throw sinsp_exception("sinsp_evt_formatter unvavailable because it was not compiled in the library"); return false; } #endif // HAS_FILTERING sinsp_evt_formatter_cache::sinsp_evt_formatter_cache(sinsp *inspector) : m_inspector(inspector) { } sinsp_evt_formatter_cache::~sinsp_evt_formatter_cache() { } bool sinsp_evt_formatter_cache::tostring(sinsp_evt *evt, string &format, OUT string *res) { 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->tostring(evt, res); } sysdig-0.19.1/userspace/libsinsp/eventformatter.h000066400000000000000000000055571316537151600221630ustar00rootroot00000000000000/* 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 . */ #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 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 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(); // 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: std::map> m_formatter_cache; sinsp *m_inspector; }; /*@}*/ sysdig-0.19.1/userspace/libsinsp/fdinfo.cpp000066400000000000000000000221041316537151600207010ustar00rootroot00000000000000/* 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 . */ #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(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(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.19.1/userspace/libsinsp/fdinfo.h000066400000000000000000000247321316537151600203570ustar00rootroot00000000000000/* 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 . */ #pragma once #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 '2' #define CHAR_FD_IPV6_SERVSOCK '3' #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: vector m_write_callbacks; 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(); 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_flags = other.m_flags; 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. */ 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; } } /*! \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; } 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; 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. 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), }; 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; } T* m_usrstate; uint32_t m_flags; 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) { 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; unordered_map m_table; // // Simple fd cache // int64_t m_last_accessed_fd; sinsp_fdinfo_t *m_last_accessed_fdinfo; }; sysdig-0.19.1/userspace/libsinsp/filter.cpp000066400000000000000000001355661316537151600207420ustar00rootroot00000000000000/* 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 . */ // // 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 "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 HAS_ANALYZER add_filter_check(new sinsp_filter_check_k8s()); #endif // HAS_ANALYZER add_filter_check(new sinsp_filter_check_mesos()); 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_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_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_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_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_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: { 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_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(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_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_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_BOOL: case PT_IPV4ADDR: 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, const filtercheck_field_info* finfo, uint32_t len) { ASSERT(rawval != NULL); ASSERT(finfo != NULL); switch(finfo->m_type) { case PT_INT8: if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { return *(int8_t *)rawval; } else if(finfo->m_print_format == PF_OCT || finfo->m_print_format == PF_HEX) { return rawval_to_string(rawval, finfo, len); } else { ASSERT(false); return Json::nullValue; } case PT_INT16: if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { return *(int16_t *)rawval; } else if(finfo->m_print_format == PF_OCT || finfo->m_print_format == PF_HEX) { return rawval_to_string(rawval, finfo, len); } else { ASSERT(false); return Json::nullValue; } case PT_INT32: if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { return *(int32_t *)rawval; } else if(finfo->m_print_format == PF_OCT || finfo->m_print_format == PF_HEX) { return rawval_to_string(rawval, finfo, len); } else { ASSERT(false); return Json::nullValue; } case PT_INT64: case PT_PID: if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { return (Json::Value::Int64)*(int64_t *)rawval; } else { return rawval_to_string(rawval, finfo, len); } case PT_L4PROTO: // This can be resolved in the future case PT_UINT8: if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { return *(uint8_t *)rawval; } else if(finfo->m_print_format == PF_OCT || finfo->m_print_format == PF_HEX) { return rawval_to_string(rawval, finfo, len); } else { ASSERT(false); return Json::nullValue; } case PT_PORT: // This can be resolved in the future case PT_UINT16: if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { return *(uint16_t *)rawval; } else if(finfo->m_print_format == PF_OCT || finfo->m_print_format == PF_HEX) { return rawval_to_string(rawval, finfo, len); } else { ASSERT(false); return Json::nullValue; } case PT_UINT32: if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { return *(uint32_t *)rawval; } else if(finfo->m_print_format == PF_OCT || finfo->m_print_format == PF_HEX) { return rawval_to_string(rawval, finfo, len); } else { ASSERT(false); return Json::nullValue; } case PT_UINT64: case PT_RELTIME: case PT_ABSTIME: if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { return (Json::Value::UInt64)*(uint64_t *)rawval; } else if( finfo->m_print_format == PF_10_PADDED_DEC || finfo->m_print_format == PF_OCT || finfo->m_print_format == PF_HEX) { return rawval_to_string(rawval, finfo, 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: return rawval_to_string(rawval, finfo, len); default: ASSERT(false); throw sinsp_exception("wrong event type " + to_string((long long) finfo->m_type)); } } char* sinsp_filter_check::rawval_to_string(uint8_t* rawval, const filtercheck_field_info* finfo, uint32_t len) { char* prfmt; ASSERT(rawval != NULL); ASSERT(finfo != NULL); switch(finfo->m_type) { case PT_INT8: if(finfo->m_print_format == PF_OCT) { prfmt = (char*)"%" PRIo8; } else if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { prfmt = (char*)"%" PRId8; } else if(finfo->m_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(finfo->m_print_format == PF_OCT) { prfmt = (char*)"%" PRIo16; } else if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { prfmt = (char*)"%" PRId16; } else if(finfo->m_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(finfo->m_print_format == PF_OCT) { prfmt = (char*)"%" PRIo32; } else if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { prfmt = (char*)"%" PRId32; } else if(finfo->m_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(finfo->m_print_format == PF_OCT) { prfmt = (char*)"%" PRIo64; } else if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { prfmt = (char*)"%" PRId64; } else if(finfo->m_print_format == PF_10_PADDED_DEC) { prfmt = (char*)"%09" PRId64; } else if(finfo->m_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(finfo->m_print_format == PF_OCT) { prfmt = (char*)"%" PRIo8; } else if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { prfmt = (char*)"%" PRIu8; } else if(finfo->m_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(finfo->m_print_format == PF_OCT) { prfmt = (char*)"%" PRIo16; } else if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { prfmt = (char*)"%" PRIu16; } else if(finfo->m_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(finfo->m_print_format == PF_OCT) { prfmt = (char*)"%" PRIo32; } else if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { prfmt = (char*)"%" PRIu32; } else if(finfo->m_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(finfo->m_print_format == PF_OCT) { prfmt = (char*)"%" PRIo64; } else if(finfo->m_print_format == PF_DEC || finfo->m_print_format == PF_ID) { prfmt = (char*)"%" PRIu64; } else if(finfo->m_print_format == PF_10_PADDED_DEC) { prfmt = (char*)"%09" PRIu64; } else if(finfo->m_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_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) finfo->m_type)); } } 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, 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, 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::set_check_id(int32_t id) { m_check_id = id; } int32_t sinsp_filter_check::get_check_id() { return m_check_id; } void sinsp_filter_check::add_filter_value(const char* str, uint32_t len, uint32_t i) { if (i >= m_val_storages.size()) { m_val_storages.push_back(vector(256)); } 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), len); m_val_storages_members.insert(item); if(len < m_val_storages_min_size) { m_val_storages_min_size = len; } if(len > m_val_storages_max_size) { m_val_storages_max_size = 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); } } void sinsp_filter_check::parse_filter_value(const char* str, uint32_t len, uint8_t *storage, uint32_t storage_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; } else { sinsp_filter_value_parser::string_to_rawval(str, len, storage, storage_len, m_field->m_type); } validate_filter_value(str, 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) { // 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; } else { return (::flt_compare(op, type, operand1, filter_value_p(), op1_len, op2_len) ); } } 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_expression implementation /////////////////////////////////////////////////////////////////////////////// sinsp_filter_expression::sinsp_filter_expression() { m_parent = NULL; } sinsp_filter_expression::~sinsp_filter_expression() { uint32_t j; for(j = 0; j < m_checks.size(); j++) { delete m_checks[j]; } } // Only filter checks get IDs int32_t sinsp_filter_expression::get_check_id() { return 0; } sinsp_filter_check* sinsp_filter_expression::allocate_new() { ASSERT(false); return NULL; } void sinsp_filter_expression::add_check(sinsp_filter_check* chk) { m_checks.push_back(chk); } void sinsp_filter_expression::parse(string expr) { } bool sinsp_filter_expression::compare(sinsp_evt *evt) { uint32_t j; uint32_t size = (uint32_t)m_checks.size(); bool res = true; sinsp_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; } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter implementation /////////////////////////////////////////////////////////////////////////////// sinsp_filter::sinsp_filter(sinsp* inspector) { m_inspector = inspector; m_filter = new sinsp_filter_expression(); m_curexpr = m_filter; } sinsp_filter::~sinsp_filter() { if(m_filter) { delete m_filter; } } void sinsp_filter::push_expression(boolop op) { sinsp_filter_expression* newexpr = new sinsp_filter_expression(); newexpr->m_boolop = op; newexpr->m_parent = m_curexpr; add_check((sinsp_filter_check*)newexpr); m_curexpr = newexpr; } void sinsp_filter::pop_expression() { ASSERT(m_curexpr->m_parent != NULL); m_curexpr = m_curexpr->m_parent; } bool sinsp_filter::run(sinsp_evt *evt) { // printf("m_filter: %p", (void*) m_filter); // ASSERT(m_filter != NULL); return m_filter->compare(evt); } void sinsp_filter::add_check(sinsp_filter_check* chk) { m_curexpr->add_check(chk); } /////////////////////////////////////////////////////////////////////////////// // 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("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() { memset(m_filter_by_evttype, 0, PPM_EVENT_MAX * sizeof(list *)); } sinsp_evttype_filter::~sinsp_evttype_filter() { 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; } } m_catchall_evttype_filters.clear(); for(const auto &val : m_evttype_filters) { delete val.second->filter; delete val.second; } m_evttype_filters.clear(); } sinsp_evttype_filter::filter_wrapper::filter_wrapper() : enabled{true} { } sinsp_evttype_filter::filter_wrapper::~filter_wrapper() { } void sinsp_evttype_filter::add(string &name, set &evttypes, set &tags, sinsp_filter *filter) { filter_wrapper *wrap = new filter_wrapper(); wrap->filter = filter; wrap->evttypes = evttypes; m_evttype_filters.insert(pair(name, wrap)); if(evttypes.size() == 0) { m_catchall_evttype_filters.push_back(wrap); } else { for(const auto &evttype: evttypes) { list *filters = m_filter_by_evttype[evttype]; if(filters == NULL) { filters = new list(); m_filter_by_evttype[evttype] = filters; } filters->push_back(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); for(const auto &val : m_evttype_filters) { if (regex_match(val.first, re)) { if(val.second->enabled.size() < (size_t) (ruleset + 1)) { val.second->enabled.resize(ruleset + 1); } val.second->enabled[ruleset] = enabled; } } } void sinsp_evttype_filter::enable_tags(const set &tags, bool enabled, uint16_t ruleset) { for(const auto &tag : tags) { for(const auto &wrap : m_filter_by_tag[tag]) { if(wrap->enabled.size() < (size_t) (ruleset + 1)) { wrap->enabled.resize(ruleset + 1); } wrap->enabled[ruleset] = enabled; } } } bool sinsp_evttype_filter::run(sinsp_evt *evt, uint16_t ruleset) { // // First run any catchall event type filters (ones that did not // explicitly specify any event type. // for(filter_wrapper *wrap : m_catchall_evttype_filters) { if(wrap->enabled.size() >= (size_t) (ruleset + 1) && wrap->enabled[ruleset] && wrap->filter->run(evt)) { return true; } } list *filters = m_filter_by_evttype[evt->m_pevt->type]; if(filters) { for(filter_wrapper *wrap : *filters) { if(wrap->enabled.size() >= (size_t) (ruleset + 1) && wrap->enabled[ruleset] && wrap->filter->run(evt)) { return true; } } } return false; } #endif // HAS_FILTERING sysdig-0.19.1/userspace/libsinsp/filter.h000066400000000000000000000131541316537151600203730ustar00rootroot00000000000000/* 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 . */ #pragma once #include #ifdef HAS_FILTERING class sinsp_filter_expression; class sinsp_filter_check; /* * 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 }; 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, }; /** @defgroup filter Filtering events * Filtering infrastructure. * @{ */ /*! \brief This is the class that runs sysdig-type filters. */ class SINSP_PUBLIC sinsp_filter { public: /*! \brief Constructs the filter. \param inspector Pointer to the inspector instance that will generate the events to be filtered. */ sinsp_filter(sinsp* inspector); ~sinsp_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(sinsp_evt *evt); void push_expression(boolop op); void pop_expression(); void add_check(sinsp_filter_check* chk); private: void parse_check(sinsp_filter_expression* parent_expr, boolop op); sinsp* m_inspector; sinsp_filter_expression* m_curexpr; sinsp_filter_expression* m_filter; 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 &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 eanble_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); private: class filter_wrapper { public: filter_wrapper(); virtual ~filter_wrapper(); sinsp_filter *filter; std::set evttypes; // Indexes from ruleset to enabled/disabled. Unlike // m_filter_by_evttype, this is managed as a vector as we're // expecting ruleset ids that are small and grouped in the range // 0..k, as compared to all possible event types. std::vector enabled; }; // Maps from event type to filter. There can be multiple // filters per event type. std::list *m_filter_by_evttype[PPM_EVENT_MAX]; // It's possible to add an event type filter with an empty // list of event types, meaning it should run for all event // types. std::list m_catchall_evttype_filters; // Maps from tag to list of filters having that tag. std::map> m_filter_by_tag; // This holds all the filters in // m_filter_by_evttype/m_catchall_evttype_filters, so they can // be cleaned up. map m_evttype_filters; }; /*@}*/ #endif // HAS_FILTERING sysdig-0.19.1/userspace/libsinsp/filter_value.h000066400000000000000000000031561316537151600215700ustar00rootroot00000000000000/* Copyright (C) 2013-2016 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 #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.19.1/userspace/libsinsp/filterchecks.cpp000066400000000000000000005550251316537151600221170ustar00rootroot00000000000000/* 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 #ifndef _WIN32 #include #endif #include "sinsp.h" #include "sinsp_int.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; /////////////////////////////////////////////////////////////////////////////// // 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; } void 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; } /////////////////////////////////////////////////////////////////////////////// // 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_IPV4ADDR, EPF_NONE, PF_NA, "fd.ip", "matches the ip address (client or server) of the fd."}, {PT_IPV4ADDR, EPF_NONE, PF_NA, "fd.cip", "client IP address."}, {PT_IPV4ADDR, EPF_NONE, PF_NA, "fd.sip", "server IP address."}, {PT_IPV4ADDR, EPF_NONE, PF_NA, "fd.lip", "local IP address."}, {PT_IPV4ADDR, 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_IPV4NET, EPF_NONE, PF_NA, "fd.net", "matches the IP network (client or server) of the fd."}, {PT_IPV4NET, EPF_NONE, PF_NA, "fd.cnet", "client IP network."}, {PT_IPV4NET, EPF_NONE, PF_NA, "fd.snet", "server IP network."}, {PT_IPV4NET, EPF_NONE, PF_NA, "fd.lnet", "local IP network."}, {PT_IPV4NET, EPF_NONE, PF_NA, "fd.rnet", "remote IP network."}, }; 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: { // // 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. // sinsp_evt enter_evt; if(!m_inspector->get_parser()->retrieve_enter_event(&enter_evt, evt)) { return false; } sinsp_evt_param *parinfo; char *name; uint32_t namelen; string sdir; parinfo = enter_evt.get_param(1); name = parinfo->m_val; namelen = parinfo->m_len; parinfo = enter_evt.get_param(0); 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) { // // 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) { *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } 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; *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } else { return NULL; } } case TYPE_DIRECTORY: { 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.resize(pos); } } else { m_tstr = "/"; } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } else { return NULL; } } 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) { if(pos < m_tstr.size() - 1) { m_tstr.resize(pos); } } else { m_tstr = "/"; } m_tstr = m_tinfo->m_container_id + ':' + m_tstr; *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } 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_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); } } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } 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_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) { ASSERT(evt); if(!extract_fd(evt)) { return NULL; } // // TYPE_FDNUM doesn't need fdinfo // if(m_field_id == TYPE_FDNUM) { return (uint8_t*)&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); } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); case TYPE_FDTYPE: if(m_fdinfo == NULL) { return NULL; } else { uint8_t *typestr = (uint8_t*)m_fdinfo->get_typestring(); if(typestr) { *len = strlen((char *) typestr); } return 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) { 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; } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } 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 = "/"; } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } case TYPE_FDTYPECHAR: if(m_fdinfo == NULL) { return extract_from_null_fd(evt, len, sanitize_strings); } 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 (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip); } } 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 (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip); } else if(evt_type == SCAP_FD_IPV4_SERVSOCK) { return (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_ip); } } break; case TYPE_LNET: case TYPE_RNET: case TYPE_LIP: case TYPE_RIP: { if(m_fdinfo == NULL) { return NULL; } scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type != SCAP_FD_IPV4_SOCK) { return NULL; } if(m_fdinfo->is_role_none()) { return NULL; } if(m_inspector->get_ifaddr_list()->is_ipv4addr_in_local_machine(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip, m_tinfo)) { if(m_field_id == TYPE_LIP) { return (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip); } else { return (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip); } } else { if(m_field_id == TYPE_LIP) { return (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip); } else { return (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip); } } } 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 (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport); } else if(evt_type == SCAP_FD_IPV6_SOCK) { return (uint8_t*)&(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; } string port = ""; if(evt_type == SCAP_FD_IPV4_SOCK) { port = 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) { port = 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); } *len = port.size(); return (uint8_t*)port.c_str(); } 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 (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport); } else if(evt_type == SCAP_FD_IPV4_SERVSOCK) { return (uint8_t*)&(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 (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport); } else if(evt_type == SCAP_FD_IPV6_SERVSOCK) { return (uint8_t*)&(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; } string port = ""; if(evt_type == SCAP_FD_IPV4_SOCK) { port = 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) { port = port_to_string(nport, this->m_fdinfo->get_l4proto(), m_inspector->m_hostname_and_port_resolution_enabled); } *len = port.size(); return (uint8_t*)port.c_str(); } 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) { return NULL; } if(m_fdinfo->is_role_none()) { return NULL; } if(m_inspector->get_ifaddr_list()->is_ipv4addr_in_local_machine(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip, m_tinfo)) { if(m_field_id == TYPE_LPORT || m_field_id == TYPE_LPROTO) { return (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport); } else { return (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport); } } else { if(m_field_id == TYPE_LPORT || m_field_id == TYPE_LPROTO) { return (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport); } else { return (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv4info.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) { return NULL; } if(m_fdinfo->is_role_none()) { return NULL; } int16_t nport = 0; if(m_inspector->get_ifaddr_list()->is_ipv4addr_in_local_machine(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip, m_tinfo)) { if(m_field_id == TYPE_LPORT || m_field_id == TYPE_LPROTO) { nport = m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport; } else { nport = m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport; } } else { if(m_field_id == TYPE_LPORT || m_field_id == TYPE_LPROTO) { nport = m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport; } else { nport = m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport; } } string port = ""; if(evt_type == SCAP_FD_IPV4_SOCK) { port = 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) { port = port_to_string(nport, this->m_fdinfo->get_l4proto(), m_inspector->m_hostname_and_port_resolution_enabled); } else { ASSERT(false); } *len = port.size(); return (uint8_t*)port.c_str(); } 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; } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } 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 { m_tbool = false; } return (uint8_t*)&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"; *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } else if(m_fdinfo->m_type == SCAP_FD_UNIX_SOCK) { m_tstr = "unix"; *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } else { return NULL; } } break; case TYPE_UID: { ASSERT(m_tinfo != NULL); m_tstr = to_string(m_tinfo->m_tid) + to_string(m_tinfo->m_lastevent_fd); *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } 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"); } } } 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) { 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; } } } 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; default: throw sinsp_exception("filter error: unsupported port comparison operator"); } } 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) { 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_NONE, 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."}, }; 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, 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 (uint8_t*)&m_dval; } return NULL; } static void 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]; if(j < nargs -1) { cmdline += ' '; } } } uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { 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 (uint8_t*)&m_u64val; case TYPE_PID: return (uint8_t*)&tinfo->m_pid; case TYPE_SID: return (uint8_t*)&tinfo->m_sid; 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(); *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } 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(); *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } } case TYPE_TTY: return (uint8_t*)&tinfo->m_tty; case TYPE_NAME: m_tstr = tinfo->get_comm(); *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); case TYPE_EXE: m_tstr = tinfo->get_exe(); *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); case TYPE_EXEPATH: m_tstr = tinfo->get_exepath(); *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); 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 += ' '; } } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } 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 += ' '; } } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } case TYPE_CMDLINE: { populate_cmdline(m_tstr, tinfo); *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } 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 += ' '; } } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } case TYPE_CWD: m_tstr = tinfo->get_cwd(); *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); case TYPE_NTHREADS: { sinsp_threadinfo* ptinfo = tinfo->get_main_thread(); if(ptinfo) { m_u64val = ptinfo->m_nchilds + 1; return (uint8_t*)&m_u64val; } else { ASSERT(false); return NULL; } } case TYPE_NCHILDS: return (uint8_t*)&tinfo->m_nchilds; case TYPE_ISMAINTHREAD: m_tbool = (uint32_t)tinfo->is_main_thread(); return (uint8_t*)&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 (uint8_t*)&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 (uint8_t*)ptot; } else { return NULL; } } case TYPE_PPID: if(tinfo->is_main_thread()) { return (uint8_t*)&tinfo->m_ptid; } else { sinsp_threadinfo* mt = tinfo->get_main_thread(); if(mt != NULL) { return (uint8_t*)&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(); *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } else { return NULL; } } case TYPE_PCMDLINE: { sinsp_threadinfo* ptinfo = m_inspector->get_thread(tinfo->m_ptid, false, true); if(ptinfo != NULL) { populate_cmdline(m_tstr, ptinfo); *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } 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 (uint8_t*)&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(); *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } 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 (uint8_t*)res; } case TYPE_DURATION: if(tinfo->m_clone_ts != 0) { m_s64val = evt->get_ts() - tinfo->m_clone_ts; ASSERT(m_s64val > 0); return (uint8_t*)&m_s64val; } else { return NULL; } case TYPE_FDOPENCOUNT: m_u64val = tinfo->get_fd_opencount(); return (uint8_t*)&m_u64val; case TYPE_FDLIMIT: m_s64val = tinfo->get_fd_limit(); return (uint8_t*)&m_s64val; case TYPE_FDUSAGE: m_dval = tinfo->get_fd_usage_pct_d(); return (uint8_t*)&m_dval; case TYPE_VMSIZE: m_u64val = tinfo->m_vmsize_kb; return (uint8_t*)&m_u64val; case TYPE_VMRSS: m_u64val = tinfo->m_vmrss_kb; return (uint8_t*)&m_u64val; case TYPE_VMSWAP: m_u64val = tinfo->m_vmswap_kb; return (uint8_t*)&m_u64val; case TYPE_THREAD_VMSIZE: if(tinfo->is_main_thread()) { m_u64val = tinfo->m_vmsize_kb; } else { m_u64val = 0; } return (uint8_t*)&m_u64val; case TYPE_THREAD_VMRSS: if(tinfo->is_main_thread()) { m_u64val = tinfo->m_vmrss_kb; } else { m_u64val = 0; } return (uint8_t*)&m_u64val; case TYPE_THREAD_VMSIZE_B: if(tinfo->is_main_thread()) { m_u64val = tinfo->m_vmsize_kb * 1024; } else { m_u64val = 0; } return (uint8_t*)&m_u64val; case TYPE_THREAD_VMRSS_B: if(tinfo->is_main_thread()) { m_u64val = tinfo->m_vmrss_kb * 1024; } else { m_u64val = 0; } return (uint8_t*)&m_u64val; case TYPE_PFMAJOR: m_u64val = tinfo->m_pfmajor; return (uint8_t*)&m_u64val; case TYPE_PFMINOR: m_u64val = tinfo->m_pfminor; return (uint8_t*)&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 += ' '; } } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } 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; *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } } return NULL; } case TYPE_VTID: if(tinfo->m_vtid == -1) { return NULL; } m_u64val = tinfo->m_vtid; return (uint8_t*)&m_u64val; case TYPE_VPID: if(tinfo->m_vpid == -1) { return NULL; } m_u64val = tinfo->m_vpid; return (uint8_t*)&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 (uint8_t*)&m_dval; } return NULL; } */ case TYPE_THREAD_CPU: { return extract_thread_cpu(evt, tinfo, true, true); } case TYPE_THREAD_CPU_USER: { return extract_thread_cpu(evt, tinfo, true, false); } case TYPE_THREAD_CPU_SYSTEM: { return extract_thread_cpu(evt, tinfo, false, true); } case TYPE_NAMETID: m_tstr = tinfo->get_comm() + to_string(evt->get_tid()); *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); 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.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_PRINT_ONLY, 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_NONE, 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, EPF_FILTER_ONLY, 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(val == "evt.abspath") { m_argid = 0; } else if(val == "evt.abspath.src") { m_argid = 1; } else if(val == "evt.abspath.dst") { m_argid = 2; } else { throw sinsp_exception("wrong syntax for evt.abspath"); } res = (int32_t)val.size() + 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; } void sinsp_filter_check_event::parse_filter_value(const char* str, uint32_t len, uint8_t *storage, uint32_t storage_len) { if(m_field_id == sinsp_filter_check_event::TYPE_ARGRAW) { ASSERT(m_arginfo != NULL); sinsp_filter_value_parser::string_to_rawval(str, len, filter_value_p(), filter_value().size(), m_arginfo->type); } else { sinsp_filter_check::parse_filter_value(str, len, storage, storage_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 == 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) { dirfdarg = "dirfd"; patharg = "name"; } else if(etype == PPME_SYSCALL_LINKAT_E) { if(m_argid == 1) { dirfdarg = "olddir"; patharg = "oldpath"; } else if(m_argid == 2) { dirfdarg = "newdir"; patharg = "newpath"; } } else if(etype == PPME_SYSCALL_UNLINKAT_E) { dirfdarg = "dirfd"; patharg = "name"; } 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 absoulte. // Some processes (e.g. irqbalance) actually do this: they pass an invalid fd and // and bsolute 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; *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); } inline uint8_t* sinsp_filter_check_event::extract_buflen(sinsp_evt *evt) { 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 (uint8_t*)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_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 (uint8_t*)&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 (uint8_t*)&m_u32val; } } } return NULL; } uint8_t* sinsp_filter_check_event::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { switch(m_field_id) { case TYPE_TIME: // if(g_filterchecks_force_raw_times) if(false) { m_strstorage = to_string(evt->get_ts()); } else { ts_to_string(evt->get_ts(), &m_strstorage, false, true); } *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); case TYPE_TIME_S: ts_to_string(evt->get_ts(), &m_strstorage, false, false); *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); case TYPE_DATETIME: ts_to_string(evt->get_ts(), &m_strstorage, true, true); *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); case TYPE_RAWTS: return (uint8_t*)&evt->m_pevt->ts; case TYPE_RAWTS_S: m_u64val = evt->get_ts() / ONE_SECOND_IN_NS; return (uint8_t*)&m_u64val; case TYPE_RAWTS_NS: m_u64val = evt->get_ts() % ONE_SECOND_IN_NS; return (uint8_t*)&m_u64val; case TYPE_RELTS: m_u64val = evt->get_ts() - m_inspector->m_firstevent_ts; return (uint8_t*)&m_u64val; case TYPE_RELTS_S: m_u64val = (evt->get_ts() - m_inspector->m_firstevent_ts) / ONE_SECOND_IN_NS; return (uint8_t*)&m_u64val; case TYPE_RELTS_NS: m_u64val = (evt->get_ts() - m_inspector->m_firstevent_ts) % ONE_SECOND_IN_NS; return (uint8_t*)&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 (uint8_t*)&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); } *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); } 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 (uint8_t*)&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 (uint8_t*)&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 (uint8_t*)&m_tsdelta; } case TYPE_RUNTIME_TIME_OUTPUT_FORMAT: { char timebuffer[100]; m_strstorage = ""; switch(m_inspector->m_output_time_flag) { case 'h': ts_to_string(evt->get_ts(), &m_strstorage, false, true); *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); 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); *len = m_strstorage.size(); return (uint8_t*) m_strstorage.c_str(); 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); *len = m_strstorage.size(); return (uint8_t*) m_strstorage.c_str(); 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"; } *len = m_strstorage.size(); return (uint8_t*) m_strstorage.c_str(); } 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; *len = m_strstorage.size(); return (uint8_t*) m_strstorage.c_str(); } } case TYPE_DIR: if(PPME_IS_ENTER(evt->get_type())) { *len = 1; return (uint8_t*)">"; } else { *len = 1; return (uint8_t*)"<"; } 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(); } *len = strlen((char *) evname); return 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 (uint8_t*)&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(); } *len = strlen((char *) evname); return 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; } *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); case TYPE_NUMBER: return (uint8_t*)&evt->m_evtnum; case TYPE_CPU: return (uint8_t*)&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->m_info->nparams) { 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) { *len = strlen(resolved_argstr); return (uint8_t*)resolved_argstr; } else { if(argstr != NULL) { *len = strlen(argstr); } return (uint8_t*)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)) { *len = strlen(il); return (uint8_t*)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 // *len = 1; return (uint8_t*)""; } 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 + ") "; } } *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); } 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); } 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) { *len = sizeof("SUCCESS"); return (uint8_t*)"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) { *len = strlen(resolved_argstr); return (uint8_t*)resolved_argstr; } else if(argstr != NULL) { *len = strlen(argstr); return (uint8_t*)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) { *len = sizeof("SUCCESS"); return (uint8_t*)"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) { *len = strlen(resolved_argstr); return (uint8_t*)resolved_argstr; } else if(argstr != NULL) { *len = strlen(argstr); return (uint8_t*)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 (uint8_t*)&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 (uint8_t*)&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 (uint8_t*)&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 (uint8_t*)&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; } *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); } case TYPE_ISWAIT: { ppm_event_flags eflags = evt->get_info_flags(); if(eflags & (EF_WAITS)) { m_u32val = 1; } else { m_u32val = 0; } } return (uint8_t*)&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 (uint8_t*)&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 (uint8_t*)&m_u32val; } case TYPE_COUNT: m_u32val = 1; return (uint8_t*)&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) { 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_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 (uint8_t*)&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 (uint8_t*)&m_u32val; } } } break; case TYPE_COUNT_THREADINFO: { uint16_t etype = evt->get_type(); if(etype == PPME_PROCINFO_E) { m_u32val = 1; return (uint8_t*)&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); } break; case TYPE_BUFLEN_OUT: if(evt->m_fdinfo && evt->get_category() == EC_IO_WRITE) { return extract_buflen(evt); } 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); } } 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); } } 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); } } 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); } } 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); } } 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); } } 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) { sinsp_evt_param *parinfo; // Just happens to be the case that // flags is the 3rd argument for // both events. parinfo = evt->get_param(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 (uint8_t*)&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]); *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); } } 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]); *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); } } 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]); *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); } } 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 = trim(subelements[1]); *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); } } } } } 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."}, }; 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) { sinsp_threadinfo* tinfo = evt->get_thread_info(); scap_userinfo* uinfo; if(tinfo == NULL) { return NULL; } if(m_field_id != TYPE_UID) { unordered_map::const_iterator it; ASSERT(m_inspector != NULL); const unordered_map* userlist = m_inspector->get_userlist(); if(tinfo->m_uid == 0xffffffff) { return NULL; } it = userlist->find(tinfo->m_uid); if(it == userlist->end()) { return NULL; } uinfo = it->second; ASSERT(uinfo != NULL); } switch(m_field_id) { case TYPE_UID: return (uint8_t*)&tinfo->m_uid; case TYPE_NAME: *len = strlen(uinfo->name); return (uint8_t*)uinfo->name; case TYPE_HOMEDIR: *len = strlen(uinfo->homedir); return (uint8_t*)uinfo->homedir; case TYPE_SHELL: *len = strlen(uinfo->shell); return (uint8_t*) uinfo->shell; 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) { sinsp_threadinfo* tinfo = evt->get_thread_info(); if(tinfo == NULL) { return NULL; } switch(m_field_id) { case TYPE_GID: return (uint8_t*)&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); *len = strlen(ginfo->name); return (uint8_t*)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_NONE, 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_NONE, 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_NONE, 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, EPF_TABLE_ONLY, 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, EPF_TABLE_ONLY, 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, EPF_TABLE_ONLY, 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, EPF_TABLE_ONLY, 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; } int64_t* sinsp_filter_check_tracer::extract_duration(uint16_t etype, sinsp_tracerparser* eparser) { 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 &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; } *len = strlen(m_storage); return (uint8_t*)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) { 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 (uint8_t*)&eparser->m_id; case TYPE_TIME: { ts_to_string(evt->get_ts(), &m_strstorage, false, true); *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); } case TYPE_NTAGS: m_u32val = (uint32_t)eparser->m_tags.size(); return (uint8_t*)&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 (uint8_t*)&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; } *len = strlen(m_storage); return (uint8_t*)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]; } } if(res) { *len = strlen(res); } return (uint8_t*)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]; } } *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); } 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); case TYPE_DURATION_HUMAN: { if(extract_duration(etype, eparser) == 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); } *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); } case TYPE_DURATION_QUANTIZED: { if(extract_duration(etype, eparser) == 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 (uint8_t*)&m_s64val; } } return NULL; } case TYPE_TAGDURATION: if((int32_t)eparser->m_tags.size() - 1 == m_argid) { return (uint8_t*)extract_duration(etype, eparser); } else { return NULL; } case TYPE_COUNT: if(evt->get_type() == PPME_TRACER_X) { m_s64val = 1; } else { m_s64val = 0; } return (uint8_t*)&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 (uint8_t*)&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 (uint8_t*)&m_s64val; case TYPE_RAWTIME: { m_strstorage = to_string(eparser->m_enter_pae->m_time); *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); } case TYPE_RAWPARENTTIME: { sinsp_partial_tracer* pepae = eparser->find_parent_enter_pae(); if(pepae == NULL) { return NULL; } m_strstorage = to_string(pepae->m_time); *len = m_strstorage.size(); return (uint8_t*)m_strstorage.c_str(); } 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_NONE, 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_NONE, 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_NONE, 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_NONE, 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_NONE, 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_NONE, 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_NONE, 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_NONE, 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 (uint8_t*)&pae->m_id; case TYPE_NTAGS: m_u32val = (uint32_t)pae->m_tags.size(); return (uint8_t*)&m_u32val; case TYPE_NARGS: m_u32val = (uint32_t)pae->m_argvals.size(); return (uint8_t*)&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; } *len = strlen(m_storage); return (uint8_t*)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]; } } if(val) { *len = strlen(val); } return (uint8_t*) 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; } *len = strlen(m_storage); return (uint8_t*)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]; } } } if(val) { *len = strlen(val); } return (uint8_t*) val; } default: ASSERT(false); break; } return NULL; } uint8_t* sinsp_filter_check_evtin::extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings) { 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) { const char *str; ASSERT(m_decoder != NULL); if(!m_decoder->is_data_valid()) { return NULL; } switch(m_field_id) { case TYPE_FACILITY: return (uint8_t*)&m_decoder->m_facility; case TYPE_FACILITY_STR: str = m_decoder->get_facility_str(); if(str) { *len = strlen(str); } return (uint8_t*)str; case TYPE_SEVERITY: return (uint8_t*)&m_decoder->m_severity; case TYPE_SEVERITY_STR: str = m_decoder->get_severity_str(); if(str) { *len = strlen(str); } return (uint8_t*)str; case TYPE_MESSAGE: *len = m_decoder->m_msg.size(); return (uint8_t*)m_decoder->m_msg.c_str(); 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_NONE, 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_NONE, 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_NONE, 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_NONE, 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_NONE, 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_NONE, 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."} }; 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) { 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; } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); case TYPE_CONTAINER_NAME: if(tinfo->m_container_id.empty()) { m_tstr = "host"; } else { sinsp_container_info container_info; bool found = m_inspector->m_container_manager.get_container(tinfo->m_container_id, &container_info); if(!found) { return NULL; } if(container_info.m_name.empty()) { return NULL; } m_tstr = container_info.m_name; } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); case TYPE_CONTAINER_IMAGE: if(tinfo->m_container_id.empty()) { return NULL; } else { sinsp_container_info container_info; bool found = m_inspector->m_container_manager.get_container(tinfo->m_container_id, &container_info); if(!found) { return NULL; } if(container_info.m_image.empty()) { return NULL; } m_tstr = container_info.m_image; } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); case TYPE_CONTAINER_IMAGE_ID: if(tinfo->m_container_id.empty()) { return NULL; } else { sinsp_container_info container_info; bool found = m_inspector->m_container_manager.get_container(tinfo->m_container_id, &container_info); if(!found) { return NULL; } if(container_info.m_imageid.empty()) { return NULL; } m_tstr = container_info.m_imageid; } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); case TYPE_CONTAINER_TYPE: if(tinfo->m_container_id.empty()) { m_tstr = "host"; } else { sinsp_container_info container_info; bool found = m_inspector->m_container_manager.get_container(tinfo->m_container_id, &container_info); if(!found) { 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_RKT: m_tstr = "rkt"; break; default: ASSERT(false); break; } } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); case TYPE_CONTAINER_PRIVILEGED: if(tinfo->m_container_id.empty()) { return NULL; } else { sinsp_container_info container_info; bool found = m_inspector->m_container_manager.get_container(tinfo->m_container_id, &container_info); if(!found) { return NULL; } // Only return a true/false value for // container types where we really know the // privileged status. if (container_info.m_type != sinsp_container_type::CT_DOCKER) { return NULL; } m_u32val = (container_info.m_privileged ? 1 : 0); } return (uint8_t*)&m_u32val; break; case TYPE_CONTAINER_MOUNTS: if(tinfo->m_container_id.empty()) { return NULL; } else { sinsp_container_info container_info; bool found = m_inspector->m_container_manager.get_container(tinfo->m_container_id, &container_info); if(!found) { 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(); } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } break; case TYPE_CONTAINER_MOUNT: if(tinfo->m_container_id.empty()) { return NULL; } else { sinsp_container_info container_info; bool found = m_inspector->m_container_manager.get_container(tinfo->m_container_id, &container_info); if(!found) { return NULL; } 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(); } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } 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 { sinsp_container_info container_info; bool found = m_inspector->m_container_manager.get_container(tinfo->m_container_id, &container_info); if(!found) { return NULL; } 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; } *len = m_tstr.size(); return (uint8_t*)m_tstr.c_str(); } break; 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, 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, 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) { switch(m_field_id) { case TYPE_CNT: m_cnt++; return (uint8_t*)&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) { 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_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_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_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); } *len = m_strval.size(); return (uint8_t*)m_strval.c_str(); } else { return NULL; } } #ifndef HAS_ANALYZER /////////////////////////////////////////////////////////////////////////////// // 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_NONE, 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_NONE, 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_NONE, 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_NONE, 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_NONE, 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_NONE, 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; } 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; } 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) { 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(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); case TYPE_K8S_POD_ID: m_tstr = pod->get_uid(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); case TYPE_K8S_POD_LABEL: { if(find_label(pod->get_labels(), m_argname, &m_tstr)) { *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; } case TYPE_K8S_POD_LABELS: { concatenate_labels(pod->get_labels(), &m_tstr); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } case TYPE_K8S_RC_NAME: { const k8s_rc_t* rc = find_rc_by_pod(pod); if(rc != NULL) { m_tstr = rc->get_name(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; } case TYPE_K8S_RC_ID: { const k8s_rc_t* rc = find_rc_by_pod(pod); if(rc != NULL) { m_tstr = rc->get_uid(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } 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)) { *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } } 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); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; } case TYPE_K8S_RS_NAME: { const k8s_rs_t* rs = find_rs_by_pod(pod); if(rs != NULL) { m_tstr = rs->get_name(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; } case TYPE_K8S_RS_ID: { const k8s_rs_t* rs = find_rs_by_pod(pod); if(rs != NULL) { m_tstr = rs->get_uid(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } 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)) { *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } } 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); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } 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()); } *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } 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()); } *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } 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()) { *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } } 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); } *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; } case TYPE_K8S_NS_NAME: { m_tstr = pod->get_namespace(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } 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(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } 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)) { *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } } 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); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; } case TYPE_K8S_DEPLOYMENT_NAME: { const k8s_deployment_t* deployment = find_deployment_by_pod(pod); if(deployment != NULL) { m_tstr = deployment->get_name(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; } case TYPE_K8S_DEPLOYMENT_ID: { const k8s_deployment_t* deployment = find_deployment_by_pod(pod); if(deployment != NULL) { m_tstr = deployment->get_uid(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } 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)) { *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } } 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); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; } default: ASSERT(false); return NULL; } return NULL; } #endif // HAS_ANALYZER /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_mesos implementation /////////////////////////////////////////////////////////////////////////////// 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_NONE, 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_NONE, 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) { sinsp_container_info container_info; bool found = m_inspector->m_container_manager.get_container(tinfo->m_container_id, &container_info); if(!found || 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) { 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(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); case TYPE_MESOS_TASK_ID: m_tstr = task->get_uid(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); case TYPE_MESOS_TASK_LABEL: if(find_label(task->get_labels(), m_argname, &m_tstr)) { *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; case TYPE_MESOS_TASK_LABELS: concatenate_labels(task->get_labels(), &m_tstr); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); case TYPE_MESOS_FRAMEWORK_NAME: { const mesos_framework* fw = find_framework_by_task(task); if(fw) { m_tstr = fw->get_name(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; } case TYPE_MESOS_FRAMEWORK_ID: { const mesos_framework* fw = find_framework_by_task(task); if(fw) { m_tstr = fw->get_uid(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; } case TYPE_MARATHON_APP_NAME: { marathon_app::ptr_t app = find_app_by_task(task); if(app != NULL) { m_tstr = app->get_name(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; } case TYPE_MARATHON_APP_ID: { marathon_app::ptr_t app = find_app_by_task(task); if(app != NULL) { m_tstr = app->get_id(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } 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)) { *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } 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); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; } case TYPE_MARATHON_GROUP_NAME: { marathon_app::ptr_t app = find_app_by_task(task); if(app) { m_tstr = app->get_group_id(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; } case TYPE_MARATHON_GROUP_ID: { marathon_app::ptr_t app = find_app_by_task(task); if(app) { m_tstr = app->get_group_id(); *len = m_tstr.size(); return (uint8_t*) m_tstr.c_str(); } break; } default: ASSERT(false); return NULL; } return NULL; } #endif // HAS_FILTERING sysdig-0.19.1/userspace/libsinsp/filterchecks.h000066400000000000000000000573731316537151600215670ustar00rootroot00000000000000/* 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 . */ #pragma once #include #include #include "filter_value.h" #include "prefix_search.h" #include "k8s.h" #include "mesos.h" #ifdef HAS_FILTERING 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); char* flt_to_string(uint8_t* rawval, filtercheck_field_info* finfo); int32_t gmt2local(time_t t); void ts_to_string(uint64_t ts, OUT string* res, bool full, bool ns); 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: 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 void 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. // 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() // 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); // // 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(); sinsp* m_inspector; bool m_needs_state_tracking = false; boolop m_boolop; cmpop m_cmpop; 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, const filtercheck_field_info* finfo, uint32_t len); Json::Value rawval_to_json(uint8_t* rawval, const filtercheck_field_info* finfo, 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); int32_t m_check_id = 0; 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 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 sinsp_filter_expression : public sinsp_filter_check { public: sinsp_filter_expression(); ~sinsp_filter_expression(); sinsp_filter_check* allocate_new(); void add_check(sinsp_filter_check* chk); // does nothing for sinsp_filter_expression void parse(string expr); bool compare(sinsp_evt *evt); // // 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) { ASSERT(false); return 0; } const filtercheck_field_info* get_field_info() { ASSERT(false); return NULL; } uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len, bool sanitize_strings = true) { ASSERT(false); return NULL; } int32_t get_check_id(); sinsp_filter_expression* m_parent; vector m_checks; }; /////////////////////////////////////////////////////////////////////////////// // 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 }; 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(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, }; 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, 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_DATETIME = 3, TYPE_RAWTS = 4, TYPE_RAWTS_S = 5, TYPE_RAWTS_NS = 6, TYPE_RELTS = 7, TYPE_RELTS_S = 8, TYPE_RELTS_NS = 9, TYPE_LATENCY = 10, TYPE_LATENCY_S = 11, TYPE_LATENCY_NS = 12, TYPE_LATENCY_QUANTIZED = 13, TYPE_LATENCY_HUMAN = 14, TYPE_DELTA = 15, TYPE_DELTA_S = 16, TYPE_DELTA_NS = 17, TYPE_RUNTIME_TIME_OUTPUT_FORMAT = 18, TYPE_DIR = 19, TYPE_TYPE = 20, TYPE_TYPE_IS = 21, TYPE_SYSCALL_TYPE = 22, TYPE_CATEGORY = 23, TYPE_CPU = 24, TYPE_ARGS = 25, TYPE_ARGSTR = 26, TYPE_ARGRAW = 27, TYPE_INFO = 28, TYPE_BUFFER = 29, TYPE_BUFLEN = 30, TYPE_RESSTR = 31, TYPE_RESRAW = 32, TYPE_FAILED = 33, TYPE_ISIO = 34, TYPE_ISIO_READ = 35, TYPE_ISIO_WRITE = 36, TYPE_IODIR = 37, TYPE_ISWAIT = 38, TYPE_WAIT_LATENCY = 39, TYPE_ISSYSLOG = 40, TYPE_COUNT = 41, TYPE_COUNT_ERROR = 42, TYPE_COUNT_ERROR_FILE = 43, TYPE_COUNT_ERROR_NET = 44, TYPE_COUNT_ERROR_MEMORY = 45, TYPE_COUNT_ERROR_OTHER = 46, TYPE_COUNT_EXIT = 47, TYPE_COUNT_PROCINFO = 48, TYPE_COUNT_THREADINFO = 49, TYPE_AROUND = 50, TYPE_ABSPATH = 51, TYPE_BUFLEN_IN = 52, TYPE_BUFLEN_OUT = 53, TYPE_BUFLEN_FILE = 54, TYPE_BUFLEN_FILE_IN = 55, TYPE_BUFLEN_FILE_OUT = 56, TYPE_BUFLEN_NET = 57, TYPE_BUFLEN_NET_IN = 58, TYPE_BUFLEN_NET_OUT = 59, TYPE_ISOPEN_READ = 60, TYPE_ISOPEN_WRITE = 61, TYPE_INFRA_DOCKER_NAME = 62, TYPE_INFRA_DOCKER_CONTAINER_ID = 63, TYPE_INFRA_DOCKER_CONTAINER_NAME = 64, TYPE_INFRA_DOCKER_CONTAINER_IMAGE = 65 }; 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); void 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); 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, }; 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 int64_t* extract_duration(uint16_t etype, sinsp_tracerparser* eparser); 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 }; 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 HAS_ANALYZER 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 // HAS_ANALYZER 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 // HAS_FILTERING sysdig-0.19.1/userspace/libsinsp/http_parser.c000066400000000000000000002131521316537151600214340ustar00rootroot00000000000000/* 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 recieving 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 automaticaly 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 delimeters */ 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.19.1/userspace/libsinsp/http_parser.h000066400000000000000000000331011316537151600214330ustar00rootroot00000000000000/* 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 /* Maximium 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 futher 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.19.1/userspace/libsinsp/http_reason.cpp000066400000000000000000000052601316537151600217660ustar00rootroot00000000000000/* 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 "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.19.1/userspace/libsinsp/http_reason.h000066400000000000000000000014241316537151600214310ustar00rootroot00000000000000/* 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 . */ #pragma once #include #include class http_reason { public: static std::string get(int status); private: static const std::map m_http_reason; }; sysdig-0.19.1/userspace/libsinsp/ifinfo.cpp000066400000000000000000000147551316537151600207230ustar00rootroot00000000000000/* 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 "sinsp.h" #include "sinsp_int.h" 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); sprintf(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); // // only handle ipv4 udp sockets // if(fd->m_type != SCAP_FD_IPV4_SOCK) { return; } 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; } } 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()) { sinsp_container_info container_info; bool found = m_inspector->m_container_manager.get_container(tinfo->m_container_id, &container_info); // // 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(found) { 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. // const unordered_map* clist = m_inspector->m_container_manager.get_containers(); for(auto it = clist->begin(); it != clist->end(); ++it) { 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++; } } 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; memcpy(info.m_addr, plist->addr, SCAP_IPV6_ADDR_LEN); memcpy(info.m_netmask, plist->netmask, SCAP_IPV6_ADDR_LEN); memcpy(info.m_bcast, plist->bcast, 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); } vector* sinsp_network_interfaces::get_ipv4_list() { return &m_ipv4_interfaces; } vector* sinsp_network_interfaces::get_ipv6_list() { return &m_ipv6_interfaces; } sysdig-0.19.1/userspace/libsinsp/ifinfo.h000066400000000000000000000044141316537151600203570ustar00rootroot00000000000000/* 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 . */ #pragma once #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() {}; char m_addr[SCAP_IPV6_ADDR_LEN]; char m_netmask[SCAP_IPV6_ADDR_LEN]; char m_bcast[SCAP_IPV6_ADDR_LEN]; string m_name; }; class SINSP_PUBLIC sinsp_network_interfaces { public: sinsp_network_interfaces(sinsp* inspector): m_inspector(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); vector* get_ipv4_list(); vector* get_ipv6_list(); inline void clear(); VISIBILITY_PRIVATE uint32_t infer_ipv4_address(uint32_t destination_address); void import_ipv4_ifaddr_list(uint32_t count, scap_ifinfo_ipv4* plist); 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.19.1/userspace/libsinsp/ifinfo_test.cpp000066400000000000000000000075561316537151600217630ustar00rootroot00000000000000/* 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 #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.19.1/userspace/libsinsp/internal_metrics.cpp000066400000000000000000000016521316537151600230030ustar00rootroot00000000000000/* 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 "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.19.1/userspace/libsinsp/internal_metrics.h000066400000000000000000000051271316537151600224510ustar00rootroot00000000000000/* 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 . */ #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.19.1/userspace/libsinsp/json_query.cpp000066400000000000000000000040121316537151600216300ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/json_query.h000066400000000000000000000033141316537151600213010ustar00rootroot00000000000000// // 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 efect // 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.19.1/userspace/libsinsp/k8s.cpp000066400000000000000000000174441316537151600201540ustar00rootroot00000000000000// // k8s.cpp // #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()); } sysdig-0.19.1/userspace/libsinsp/k8s.h000066400000000000000000000056701316537151600176170ustar00rootroot00000000000000// // k8s.h // // extracts needed data from the k8s REST API interface // #pragma once #include "json/json.h" #include "k8s_common.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.19.1/userspace/libsinsp/k8s_api_error.cpp000066400000000000000000000015421316537151600222060ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_api_error.h000066400000000000000000000041431316537151600216530ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_api_handler.cpp000066400000000000000000000043421316537151600224730ustar00rootroot00000000000000// // k8s_api_handler.cpp // #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 sysdig-0.19.1/userspace/libsinsp/k8s_api_handler.h000066400000000000000000000016761316537151600221470ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_common.h000066400000000000000000000011101316537151600211500ustar00rootroot00000000000000// // k8s_common.h // #pragma once // // Macros for enable/disable k8s threading // Used to eliminate mutex locking when running single-threaded // #ifndef K8S_DISABLE_THREAD #ifdef HAS_ANALYZER #warning "K8S watch in thread is experimental" #else #error "K8S watch in thread not supported. Please #define K8S_DISABLE_THREAD" #endif // HAS_ANALYZER #include #define K8S_DECLARE_MUTEX mutable std::mutex m_mutex #define K8S_LOCK_GUARD_MUTEX std::lock_guard lock(m_mutex) #else #define K8S_DECLARE_MUTEX #define K8S_LOCK_GUARD_MUTEX #endif // K8S_DISABLE_THREAD sysdig-0.19.1/userspace/libsinsp/k8s_component.cpp000066400000000000000000000615261316537151600222360ustar00rootroot00000000000000// // 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) { } bool k8s_pod_t::has_container_id(const std::string& container_id) { for(const auto& c : m_container_ids) { if(c == container_id) { return true; } } return false; } std::string* k8s_pod_t::get_container_id(const std::string& container_id) { for(auto& c : m_container_ids) { if(c == container_id) { return &c; } } return 0; } k8s_container* k8s_pod_t::get_container(const std::string& container_name) { for(auto& c : m_containers) { if(c.get_name() == container_name) { return &c; } } return 0; } // // 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 = sinsp_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 = sinsp_logger::SEV_EVT_INFORMATION; if(sev == "Warning") { severity = sinsp_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"; g_logger.log(sinsp_user_event::to_string(epoch_time_evt_s, std::move(event_name), std::move(description), std::move(scope), std::move(tags)), 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.19.1/userspace/libsinsp/k8s_component.h000066400000000000000000000547451316537151600217100ustar00rootroot00000000000000// // 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 #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); std::string* get_container_id(const std::string& container_id); k8s_container* get_container(const std::string& container_name); // 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; bool has_container_id(const std::string& container_id); 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 sinsp_logger::event_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.19.1/userspace/libsinsp/k8s_daemonset_handler.cpp000066400000000000000000000071761316537151600237110ustar00rootroot00000000000000// // k8s_daemonset_handler.cpp // #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; } sysdig-0.19.1/userspace/libsinsp/k8s_daemonset_handler.h000066400000000000000000000013671316537151600233520ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_deployment_handler.cpp000066400000000000000000000071161316537151600241040ustar00rootroot00000000000000// // k8s_deployment_handler.cpp // #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; } sysdig-0.19.1/userspace/libsinsp/k8s_deployment_handler.h000066400000000000000000000013741316537151600235510ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_dispatcher.cpp000066400000000000000000000541261316537151600223600ustar00rootroot00000000000000// // k8s_dispatcher.cpp // #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); } sysdig-0.19.1/userspace/libsinsp/k8s_dispatcher.h000066400000000000000000000131621316537151600220200ustar00rootroot00000000000000// // k8s_dispatcher.h // // kubernetes REST API notification abstraction // #pragma once #include "k8s_common.h" #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.19.1/userspace/libsinsp/k8s_event_data.cpp000066400000000000000000000011551316537151600223360ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_event_data.h000066400000000000000000000013101316537151600217740ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_event_handler.cpp000066400000000000000000000212311316537151600230370ustar00rootroot00000000000000// // k8s_event_handler.cpp // #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); } } sysdig-0.19.1/userspace/libsinsp/k8s_event_handler.h000066400000000000000000000016601316537151600225100ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_handler.cpp000066400000000000000000000526721316537151600216530ustar00rootroot00000000000000// // k8s_handler.cpp // #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 } sysdig-0.19.1/userspace/libsinsp/k8s_handler.h000066400000000000000000000205301316537151600213040ustar00rootroot00000000000000// // 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. namepace, 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.19.1/userspace/libsinsp/k8s_namespace_handler.cpp000066400000000000000000000052361316537151600236610ustar00rootroot00000000000000// // k8s_namespace_handler.cpp // #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; } sysdig-0.19.1/userspace/libsinsp/k8s_namespace_handler.h000066400000000000000000000012701316537151600233200ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_net.cpp000066400000000000000000000200611316537151600210070ustar00rootroot00000000000000// // k8s_net.cpp // #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 sysdig-0.19.1/userspace/libsinsp/k8s_net.h000066400000000000000000000106001316537151600204520ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_node_handler.cpp000066400000000000000000000061051316537151600226460ustar00rootroot00000000000000// // k8s_node_handler.cpp // #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; } sysdig-0.19.1/userspace/libsinsp/k8s_node_handler.h000066400000000000000000000013031316537151600223060ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_pod_handler.cpp000066400000000000000000000143101316537151600225000ustar00rootroot00000000000000// // k8s_pod_handler.cpp // #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," " 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," " 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%3DRunning", 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()); } } } return container_list; } k8s_container::list k8s_pod_handler::extract_pod_containers(const Json::Value& item) { k8s_container::list ext_containers; 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; } sysdig-0.19.1/userspace/libsinsp/k8s_pod_handler.h000066400000000000000000000017611316537151600221530ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_replicaset_handler.cpp000066400000000000000000000070171316537151600240570ustar00rootroot00000000000000// // k8s_replicaset_handler.cpp // #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; } sysdig-0.19.1/userspace/libsinsp/k8s_replicaset_handler.h000066400000000000000000000013741316537151600235240ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_replicationcontroller_handler.cpp000066400000000000000000000071611316537151600263410ustar00rootroot00000000000000// // k8s_replicationcontroller_handler.cpp // #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; } sysdig-0.19.1/userspace/libsinsp/k8s_replicationcontroller_handler.h000066400000000000000000000014501316537151600260010ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_service_handler.cpp000066400000000000000000000135561316537151600233710ustar00rootroot00000000000000// // k8s_service_handler.cpp // #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; } sysdig-0.19.1/userspace/libsinsp/k8s_service_handler.h000066400000000000000000000015321316537151600230250ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/k8s_state.cpp000066400000000000000000000455101316537151600213470ustar00rootroot00000000000000// // k8s_state.cpp // #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 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; } throw sinsp_exception("Invalid container ID (expected '" + m_docker_prefix + "{ID}' or '" + m_rkt_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; } sysdig-0.19.1/userspace/libsinsp/k8s_state.h000066400000000000000000000347571316537151600210270ustar00rootroot00000000000000// // 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 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.19.1/userspace/libsinsp/logger.cpp000066400000000000000000000100731316537151600207150ustar00rootroot00000000000000/* 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 . */ #ifndef _WIN32 #include #else #include #endif #include #include "sinsp.h" #include "sinsp_int.h" /////////////////////////////////////////////////////////////////////////////// // sinsp_logger implementation /////////////////////////////////////////////////////////////////////////////// sinsp_logger::sinsp_logger() { m_file = NULL; m_flags = OT_NONE; m_sev = SEV_INFO; m_callback = NULL; } sinsp_logger::~sinsp_logger() { if(m_file) { ASSERT(m_flags & sinsp_logger::OT_FILE); fclose(m_file); } } void sinsp_logger::set_log_output_type(sinsp_logger::output_type log_output_type) { if(log_output_type & (sinsp_logger::OT_STDOUT | sinsp_logger::OT_STDERR)) { m_flags = log_output_type; } else if(log_output_type == sinsp_logger::OT_STDERR) { add_file_log("sinsp.log"); } else if(log_output_type == sinsp_logger::OT_NONE) { return; } else { ASSERT(false); throw sinsp_exception("invalid log output type"); } } void sinsp_logger::add_stdout_log() { ASSERT((m_flags & sinsp_logger::OT_STDERR) == 0); m_flags |= sinsp_logger::OT_STDOUT; } void sinsp_logger::add_stderr_log() { ASSERT((m_flags & sinsp_logger::OT_STDOUT) == 0); m_flags |= sinsp_logger::OT_STDERR; } void sinsp_logger::add_file_log(string filename) { ASSERT(m_file == NULL); 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::add_callback_log(sinsp_logger_callback callback) { ASSERT(m_callback == NULL); m_callback = callback; m_flags |= sinsp_logger::OT_CALLBACK; } void sinsp_logger::remove_callback_log() { m_callback = 0; m_flags &= ~sinsp_logger::OT_CALLBACK; } void sinsp_logger::set_severity(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(string msg, event_severity sev) { if(is_callback()) { (*m_callback)(std::move(msg), (uint32_t)sev); } } void sinsp_logger::log(string msg, severity sev) { if((sev > m_sev) || is_user_event(sev)) { return; } if((m_flags & sinsp_logger::OT_NOTS) == 0) { struct timeval ts; gettimeofday(&ts, NULL); time_t rawtime = (time_t)ts.tv_sec; struct tm* time_info = gmtime(&rawtime); snprintf(m_tbuf, sizeof(m_tbuf), "%.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); msg.insert(0, m_tbuf, 22); } if(is_callback() && m_callback) { (*m_callback)(std::move(msg), (uint32_t)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); } } char* sinsp_logger::format(severity sev, const char* fmt, ...) { if(!is_callback() && is_user_event(sev)) { m_tbuf[0] = '\0'; return m_tbuf; } va_list ap; va_start(ap, fmt); vsnprintf(m_tbuf, sizeof(m_tbuf), fmt, ap); va_end(ap); log(m_tbuf, sev); return m_tbuf; } char* sinsp_logger::format(const char* fmt, ...) { va_list ap; va_start(ap, fmt); vsnprintf(m_tbuf, sizeof(m_tbuf), fmt, ap); va_end(ap); log(m_tbuf, SEV_INFO); return m_tbuf; } sysdig-0.19.1/userspace/libsinsp/logger.h000066400000000000000000000053301316537151600203620ustar00rootroot00000000000000/* 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 . */ #pragma once /////////////////////////////////////////////////////////////////////////////// // The logger class /////////////////////////////////////////////////////////////////////////////// typedef void (*sinsp_logger_callback)(std::string&& str, uint32_t sev); class SINSP_PUBLIC sinsp_logger { public: static const uint32_t SEVERITY_NONE = (uint32_t)-1; 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, SEV_MIN = SEV_FATAL, SEV_MAX = SEV_TRACE }; enum event_severity { SEV_EVT_EMERGENCY = 10, SEV_EVT_FATAL = 11, SEV_EVT_CRITICAL = 12, SEV_EVT_ERROR = 13, SEV_EVT_WARNING = 14, SEV_EVT_NOTICE = 15, SEV_EVT_INFORMATION = 16, SEV_EVT_DEBUG = 17, SEV_EVT_MIN = SEV_EVT_EMERGENCY, SEV_EVT_MAX = SEV_EVT_DEBUG }; enum event_memdump_severity { SEV_EVT_MDUMP = SEV_EVT_MAX + 1 }; enum output_type { OT_NONE = 0, OT_STDOUT = 1, OT_STDERR = 2, OT_FILE = 4, OT_CALLBACK = 8, OT_NOTS = 256, }; sinsp_logger(); ~sinsp_logger(); void set_log_output_type(sinsp_logger::output_type log_output_type); void add_stdout_log(); void add_stderr_log(); void add_file_log(string filename); void add_file_log(FILE* f); void add_callback_log(sinsp_logger_callback callback); void remove_callback_log(); void set_severity(severity sev); severity get_severity() const; void log(string msg, severity sev=SEV_INFO); void log(string msg, event_severity sev); // Log functions that accept printf syntax and return the formatted buffer. char* format(severity sev, const char* fmt, ...); char* format(const char* fmt, ...); private: bool is_callback() const; bool is_user_event(severity sev) const; FILE* m_file; sinsp_logger_callback m_callback; uint32_t m_flags; severity m_sev; char m_tbuf[32768]; }; inline bool sinsp_logger::is_callback() const { return (m_flags & sinsp_logger::OT_CALLBACK) != 0; } inline bool sinsp_logger::is_user_event(severity sev) const { return (static_cast(sev) >= static_cast(SEV_EVT_MIN) && static_cast(sev) <= static_cast(SEV_EVT_MAX)); } sysdig-0.19.1/userspace/libsinsp/lua_parser.cpp000066400000000000000000000023431316537151600215740ustar00rootroot00000000000000#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(sinsp* inspector, lua_State *ls) { m_inspector = inspector; m_ls = ls; reset(); // Register our c++ defined functions luaL_openlib(m_ls, "filter", ll_filter, 0); lua_pushlightuserdata(m_ls, this); lua_setglobal(m_ls, "siparser"); } void lua_parser::reset() { m_have_rel_expr = false; m_last_boolop = BO_NONE; m_nest_level = 0; m_filter = new sinsp_filter(m_inspector); } sinsp_filter* lua_parser::get_filter(bool reset_filter) { if (m_nest_level != 0) { throw sinsp_exception("Error in configured filter: unbalanced nesting"); } sinsp_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; } sysdig-0.19.1/userspace/libsinsp/lua_parser.h000066400000000000000000000006371316537151600212450ustar00rootroot00000000000000#pragma once #include "sinsp.h" typedef struct lua_State lua_State; class lua_parser { public: lua_parser(sinsp* inspector, lua_State *ls); ~lua_parser(); sinsp_filter* get_filter(bool reset_filter = false); private: void reset(); sinsp* m_inspector; sinsp_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.19.1/userspace/libsinsp/lua_parser_api.cpp000066400000000000000000000126311316537151600224260ustar00rootroot00000000000000#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. 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, "in") == 0) { return CO_IN; } else if(strcmp(str, "pmatch") == 0) { return CO_PMATCH; } else if(strcmp(str, "exists") == 0) { return CO_EXISTS; } 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_getglobal(ls, "siparser"); lua_parser* parser = (lua_parser*)lua_touserdata(ls, -1); lua_pop(ls, 1); 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()"; fprintf(stderr, "%s\n", err.c_str()); throw sinsp_exception(err); } sinsp_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; return 0; } int lua_parser_cbacks::unnest(lua_State *ls) { lua_getglobal(ls, "siparser"); lua_parser* parser = (lua_parser*)lua_touserdata(ls, -1); lua_pop(ls, 1); if (parser->m_nest_level < 1) { string err = "filter.unnest() called without being nested"; fprintf(stderr, "%s\n", err.c_str()); throw sinsp_exception(err); } sinsp_filter* filter = parser->m_filter; filter->pop_expression(); parser->m_nest_level--; return 0; } int lua_parser_cbacks::bool_op(lua_State *ls) { lua_getglobal(ls, "siparser"); lua_parser* parser = (lua_parser*)lua_touserdata(ls, -1); lua_pop(ls, 1); 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() "; fprintf(stderr, "%s\n", err.c_str()); 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"; fprintf(stderr, "%s\n", err.c_str()); throw sinsp_exception(err); } } parser->m_last_boolop = op; return 0; } int lua_parser_cbacks::rel_expr(lua_State *ls) { lua_getglobal(ls, "siparser"); lua_parser* parser = (lua_parser*)lua_touserdata(ls, -1); lua_pop(ls, 1); if (parser->m_have_rel_expr && parser->m_last_boolop == BO_NONE) { string err = "filter.rel_expr() called twice in a row"; fprintf(stderr, "%s\n", err.c_str()); throw sinsp_exception(err); } parser->m_have_rel_expr = true; sinsp* inspector = parser->m_inspector; sinsp_filter* filter = parser->m_filter; const char* fld = luaL_checkstring(ls, 1); sinsp_filter_check* chk = g_filterlist.new_filter_check_from_fldname(fld, inspector, true); if(chk == NULL) { string err = "filter_check called with nonexistent field " + string(fld); fprintf(stderr, "%s\n", err.c_str()); throw sinsp_exception("parser API error"); } try { 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, 2); 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, 3)) { string err = "Got non-table as in-expression operand\n"; fprintf(stderr, "%s\n", err.c_str()); throw sinsp_exception("parser API error"); } int n = luaL_getn(ls, 3); /* get size of table */ for (i=1; i<=n; i++) { lua_rawgeti(ls, 3, i); const char* value = luaL_checkstring(ls, 5); chk->add_filter_value(value, strlen(value), i - 1); lua_pop(ls, 1); } } else { const char* value = luaL_checkstring(ls, 3); chk->add_filter_value(value, strlen(value)); } if (lua_isnumber(ls, 4)) { rule_index = (int) luaL_checkinteger(ls, 4); } } else { if (lua_isnumber(ls, 3)) { rule_index = (int) luaL_checkinteger(ls, 3); } } if (rule_index) { chk->set_check_id(rule_index); } } catch(sinsp_exception& e) { fprintf(stderr, "Error in filter.rel_expr() %s\n\n", e.what()); throw e; } filter->add_check(chk); return 0; } sysdig-0.19.1/userspace/libsinsp/lua_parser_api.h000066400000000000000000000022521316537151600220710ustar00rootroot00000000000000/* 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 . */ #pragma once extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" } 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.19.1/userspace/libsinsp/marathon_component.cpp000066400000000000000000000154731316537151600233420ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/marathon_component.h000066400000000000000000000124161316537151600230010ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/marathon_http.cpp000066400000000000000000000045271316537151600223150ustar00rootroot00000000000000// // marathon_http.cpp // #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 "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; CURLcode res = get_data(make_uri("/v2/info"), os); if(res != CURLE_OK) { g_logger.log(curl_easy_strerror(res), sinsp_logger::SEV_ERROR); 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 { g_logger.log("Error parsing framework info.\nJSON:\n---\n" + os.str() + "\n---", sinsp_logger::SEV_ERROR); return false; } } catch(std::exception& ex) { g_logger.log(std::string("Error parsing framework info:") + ex.what(), sinsp_logger::SEV_ERROR); return false; } return true; } std::string marathon_http::get_groups(const std::string& group_id) { std::ostringstream os; CURLcode res = get_data(make_uri("/v2/groups" + group_id), os); if(res != CURLE_OK) { g_logger.log(curl_easy_strerror(res), sinsp_logger::SEV_ERROR); return ""; } return os.str(); } #endif // HAS_CAPTURE sysdig-0.19.1/userspace/libsinsp/marathon_http.h000066400000000000000000000010001316537151600217410ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/memmem.cpp000066400000000000000000000021531316537151600207130ustar00rootroot00000000000000/* 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 . */ #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.19.1/userspace/libsinsp/mesos.cpp000066400000000000000000000671031316537151600205720ustar00rootroot00000000000000// // mesos.cpp // #include "mesos.h" #include "mesos_component.h" #include "sinsp.h" #include "sinsp_int.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); 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), framework_id); set_marathon_apps_json(dummy_group/*mesos_http::try_parse(marathon_apps_json)*/, 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() { #ifdef HAS_CAPTURE curl_global_cleanup(); #endif // HAS_CAPTURE } void mesos::init() { #ifdef HAS_CAPTURE if(!m_mesos_uri.empty()) { curl_global_init(CURL_GLOBAL_DEFAULT); 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) { g_logger.log("Detected null Marathon app (" + app_it->first + "), resetting current state.", sinsp_logger::SEV_WARNING); 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); } } } } else { g_logger.log("Tasks is null", sinsp_logger::SEV_ERROR); } } 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 { g_logger.log("Received invalid Marathon apps JSON", sinsp_logger::SEV_WARNING); } 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."); } } } } } sysdig-0.19.1/userspace/libsinsp/mesos.h000066400000000000000000000154361316537151600202410ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/mesos_auth.cpp000066400000000000000000000056741316537151600216200ustar00rootroot00000000000000/* Copyright (C) 2013-2016 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 . */ // // mesos_auth.cpp // #include #include "mesos_auth.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 { 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) { g_logger.format(sinsp_logger::SEV_ERROR, "Could not fetch authentication token via %s: %s", m_auth_uri.to_string().c_str(), e.what()); } #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 } sysdig-0.19.1/userspace/libsinsp/mesos_auth.h000066400000000000000000000027671316537151600212650ustar00rootroot00000000000000/* Copyright (C) 2013-2016 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 . */ // // 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.19.1/userspace/libsinsp/mesos_collector.cpp000066400000000000000000000105621316537151600226350ustar00rootroot00000000000000// // mesos_collector.cpp // #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 = strerror(errno); g_logger.log("Mesos collector select error, removing all sockets (" + err + ')', sinsp_logger::SEV_ERROR); 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()) { g_logger.log("Mesos collector data handling error, removing Marathon socket for framework [" + fid + ']', sinsp_logger::SEV_ERROR); } else { g_logger.log("Mesos collector data handling error, removing Mesos state socket.", sinsp_logger::SEV_ERROR); } remove(it); continue; } } } else { FD_SET(it->first, &m_infd); } if(FD_ISSET(it->first, &m_errfd)) { if(errno != EAGAIN) { std::string err = strerror(errno); g_logger.log(err, sinsp_logger::SEV_ERROR); std::string fid; if(it->second) { it->second->on_error(err, true); fid = it->second->get_framework_id(); } if(!fid.empty()) { g_logger.log("Mesos collector socket error, removing Marathon socket for framework [" + fid + ']', sinsp_logger::SEV_ERROR); } else { g_logger.log("Mesos collector socket error, removing Mesos state socket.", sinsp_logger::SEV_ERROR); } remove(it); continue; } } else { FD_SET(it->first, &m_errfd); } ++it; } } } else { g_logger.log("Mesos collector is empty. Stopping.", sinsp_logger::SEV_ERROR); m_stopped = true; return; } } if(!m_loop) { break; } } } catch(std::exception& ex) { g_logger.log(std::string("Mesos collector error: ") + ex.what(), sinsp_logger::SEV_ERROR); remove_all(); m_stopped = true; } } #endif // HAS_CAPTURE sysdig-0.19.1/userspace/libsinsp/mesos_collector.h000066400000000000000000000017511316537151600223020ustar00rootroot00000000000000// // 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_CAPTUREsysdig-0.19.1/userspace/libsinsp/mesos_common.h000066400000000000000000000000501316537151600215730ustar00rootroot00000000000000// // mesos_common.h // #pragma once sysdig-0.19.1/userspace/libsinsp/mesos_component.cpp000066400000000000000000000146001316537151600226460ustar00rootroot00000000000000// // mesos_component.cpp // #include "mesos_component.h" #include "marathon_component.h" #include "sinsp.h" #include "sinsp_int.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); } } // // slave // mesos_slave::mesos_slave(const std::string& name, const std::string& uid) : mesos_component(mesos_component::MESOS_SLAVE, name, uid) { } sysdig-0.19.1/userspace/libsinsp/mesos_component.h000066400000000000000000000121611316537151600223130ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/mesos_http.cpp000066400000000000000000000551451316537151600216340ustar00rootroot00000000000000// // mesos_http.cpp // #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 "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 { g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); throw sinsp_exception("mesos_http: Mesos master leader detection failed in get_state_frameworks(): Invalid JSON."); } } 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 { g_logger.log(os.str(), sinsp_logger::SEV_DEBUG); throw sinsp_exception("mesos_http: Mesos master leader [" + m_url.to_string(false) + "] detection failed: Invalid JSON."); } } 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)) { g_logger.log("mesos_http: Can not obtain URL for Marathon framework.", sinsp_logger::SEV_ERROR); } } } 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) { g_logger.log(curl_easy_strerror(res), sinsp_logger::SEV_ERROR); 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 { g_logger.log("mesos_http: Mesos or Marathon Invalid JSON received from [" + m_url.to_string(false) + ']', sinsp_logger::SEV_WARNING); g_logger.log("JSON: <" + os.str() + '>', sinsp_logger::SEV_DEBUG); } 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\n\0"); 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)) { g_logger.log("mesos_http: Invalid Mesos or Marathon JSON data detected (chunked transfer).", sinsp_logger::SEV_ERROR); (m_mesos.*m_callback_func)(nullptr, m_framework_id); } else { (m_mesos.*m_callback_func)(try_parse(m_data_buf), 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) { (m_mesos.*m_callback_func)(nullptr, m_framework_id); m_data_buf.clear(); g_logger.log("Invalid HTTP content length from [: " + m_url.to_string(false) + ']' + std::to_string(len), sinsp_logger::SEV_ERROR); return false; } else { m_content_length = static_cast(len); } } } } return true; } void mesos_http::extract_data(std::string& data) { if(!detect_chunked_transfer(data)) { g_logger.log("mesos_http: An error occurred while detecting chunked transfer.", sinsp_logger::SEV_ERROR); 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]; std::string data; CURLcode ret; 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) { g_logger.log(std::string("mesos_http: Data receive error [" + m_url.to_string() + "]: ").append(ex.what()), sinsp_logger::SEV_ERROR); return false; } return true; connection_closed: g_logger.log("mesos_http: Mesos or Marathon API connection [" + m_url.to_string() + "] closed.", sinsp_logger::SEV_ERROR); 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; CURLcode res = get_data(make_uri("/master/tasks"), os); Json::Value labels; if(res != CURLE_OK) { g_logger.log(curl_easy_strerror(res), sinsp_logger::SEV_ERROR); 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 { g_logger.log("mesos_http: Error parsing tasks.\nJSON:\n---\n" + os.str() + "\n---", sinsp_logger::SEV_ERROR); } } catch(std::exception& ex) { g_logger.log(std::string("mesos_http: Error parsing tasks:") + ex.what(), sinsp_logger::SEV_ERROR); } return labels; } #endif // HAS_CAPTURE sysdig-0.19.1/userspace/libsinsp/mesos_http.h000066400000000000000000000123511316537151600212710ustar00rootroot00000000000000// // 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" 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); 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) { json_ptr_t root(new Json::Value()); try { if(Json::Reader().parse(json, *root)) { return root; } } 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) { json_ptr_t root(new Json::Value()); try { if(Json::Reader().parse(json, *root)) { return root; } } catch(...) { } return nullptr; } }; #endif // HAS_CAPTURE sysdig-0.19.1/userspace/libsinsp/mesos_state.cpp000066400000000000000000000306711316537151600217720ustar00rootroot00000000000000// // k8s_state.cpp // #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) { g_logger.log("Could not find or create app [" + app_id + ']', sinsp_logger::SEV_ERROR); 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 { g_logger.log("Task [" + task_id + "] can not be obtained (null). Task not added to app [" + app->get_id() + ']', sinsp_logger::SEV_ERROR); } } else { g_logger.log("Attempt to add task [" + task_id + "] to non-existing (null) app.", sinsp_logger::SEV_ERROR); } } 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 { g_logger.log("An error occurred adding app [" + app_id.asString() + "] to group [" + id + ']', sinsp_logger::SEV_ERROR); } } } } } 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 { g_logger.log("Marathon task not found in mesos state: " + tid, sinsp_logger::SEV_WARNING); } } } } } else { g_logger.log("NOT added app [" + id + "] to Marathon group: [" + group_id + ']', sinsp_logger::SEV_ERROR); } } else { g_logger.log("Could not determine group ID for app: " + id, sinsp_logger::SEV_ERROR); } } return p_app; } sysdig-0.19.1/userspace/libsinsp/mesos_state.h000066400000000000000000000246641316537151600214440ustar00rootroot00000000000000// // 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 #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)) { g_logger.log("Task [" + uid + "] not found in Marathon app [" + app_id + ']', sinsp_logger::SEV_ERROR); } } else { g_logger.log("Group not found for Marathon app [" + app_id + "] while trying to remove task [" + uid + ']', sinsp_logger::SEV_ERROR); } } 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.19.1/userspace/libsinsp/parsers.cpp000066400000000000000000003056341316537151600211270ustar00rootroot00000000000000/* 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 . */ #ifdef _WIN32 #define NOMINMAX #include #else #include #include #ifdef _DEBUG #endif // _DEBUG #include #endif // _WIN32 #include #include #include #include #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 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 && 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_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_SENDTO_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: 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: 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: 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; 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; case PPME_SYSCALL_CHROOT_X: parse_chroot_exit(evt); break; case PPME_SYSCALL_SETSID_X: parse_setsid_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; } } } 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; } 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->m_info->nparams != 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[0] == 'f' && evt->m_info->params[0].name[1] == 'd'))) { 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 succesfully 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 correspoding 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); } if(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(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; tinfo.m_tty = ptinfo->m_tty; } 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_tty = ptinfo->m_tty; } 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()); // // 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->m_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; } // // Initilaize the thread clone time // tinfo.m_clone_ts = evt->get_ts(); // // Add the new thread to the table // 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 #ifdef _DEBUG g_logger.format(sinsp_logger::SEV_INFO, "tid collision for %" PRIu64 "(%s)", tinfo.m_tid, tinfo.m_comm.c_str()); #endif } 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: // 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: // 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: // 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: // 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: // 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); } // // 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 absoulte. // Some processes (e.g. irqbalance) actually do this: they pass an invalid fd and // and bsolute 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()) { g_logger.log(std::string("An event scheduled but no events available." "All pending event requests for " "[") + typeid(T).name() + "] are cancelled.", sinsp_logger::SEV_ERROR); 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; 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 } 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 } 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; ASSERT(evt->m_tinfo); if(evt->m_tinfo == nullptr) { return; } // // 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(evt->get_type() == 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; sdir = evt->m_tinfo->get_cwd(); } else if(evt->get_type() == PPME_SYSCALL_CREAT_X) { parinfo = evt->get_param(1); name = parinfo->m_val; namelen = parinfo->m_len; flags = 0; sdir = evt->m_tinfo->get_cwd(); } else if(evt->get_type() == 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 { 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.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; if(protocol == IPPROTO_TCP) { fdi.m_sockinfo.m_ipv4info.m_fields.m_l4proto = SCAP_L4_TCP; } else if(protocol == IPPROTO_UDP) { fdi.m_sockinfo.m_ipv4info.m_fields.m_l4proto = 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) { fdi.m_sockinfo.m_ipv4info.m_fields.m_l4proto = SCAP_L4_TCP; } else if((type & 0xff) == SOCK_DGRAM) { fdi.m_sockinfo.m_ipv4info.m_fields.m_l4proto = SCAP_L4_UDP; } else { ASSERT(false); } } else if(protocol == IPPROTO_ICMP) { fdi.m_sockinfo.m_ipv4info.m_fields.m_l4proto = SCAP_L4_ICMP; } } 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) { g_logger.log("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, sinsp_logger::SEV_DEBUG); } #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); } 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 mnaually 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) { 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_port = port; } } else if (family == PPM_AF_INET6) { uint16_t port = *(uint16_t *)(packed_data + 17); if(port > 0) { evt->m_fdinfo->m_type = SCAP_FD_IPV6_SERVSOCK; evt->m_fdinfo->m_sockinfo.m_ipv6serverinfo.m_port = port; } } // // 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; 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) { // // connections that return with a SE_EINPROGRESS are totally legit. // if(retval != -SE_EINPROGRESS) { 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; // // 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) { // // For the moment, we only support IPv4-mapped IPv6 addresses // (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_name = evt->get_param_as_str(1, &parstr, sinsp_evt::PF_SIMPLE); evt->m_fdinfo->m_type = SCAP_FD_IPV6_SOCK; return; } evt->m_fdinfo->m_type = SCAP_FD_IPV4_SOCK; } // // 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(evt->m_fdinfo->m_type == SCAP_FD_IPV4_SOCK || evt->m_fdinfo->m_type == SCAP_FD_IPV4_SERVSOCK); #ifndef HAS_ANALYZER // // Update the FD info with this tuple // if(family == PPM_AF_INET) { m_inspector->m_parser->set_ipv4_addresses_and_ports(evt->m_fdinfo, packed_data); } else { m_inspector->m_parser->set_ipv4_mapped_ipv6_addresses_and_ports(evt->m_fdinfo, packed_data); } #endif // // Add the friendly name to the fd info // 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 } // // 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) { // // We only support IPv4-mapped IPv6 addresses (http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses) // for the moment // 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 { fdi.m_type = SCAP_FD_IPV6_SOCK; } } 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(); // // 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 cleanung up the FD and removing it from all the tables // (process FD table, connection table...). // It's invoked when a close() or a threadexit 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 succesfully 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_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) { // // For the moment, we only support IPv4-mapped IPv6 addresses // (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))) { return false; } 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 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_sockinfo.m_ipv4info.m_fields.m_l4proto == SCAP_L4_UNKNOWN) { evt->m_fdinfo->m_sockinfo.m_ipv4info.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_ipv4_addresses(sinsp_fdinfo_t* fdinfo) { 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; } 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; 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(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) { 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_ipv4_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) { 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_ipv4_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); } } } } } 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 correspoding 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)); 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()) { 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()) { 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; 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; evt->get_thread_info()->m_gid = new_egid; } } 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); g_logger.log(json, sinsp_logger::SEV_DEBUG); 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(!id.isNull() && id.isConvertibleTo(Json::stringValue)) { container_info.m_id = id.asString(); } const Json::Value& type = container["type"]; if(!type.isNull() && type.isConvertibleTo(Json::uintValue)) { container_info.m_type = static_cast(type.asUInt()); } const Json::Value& name = container["name"]; if(!name.isNull() && name.isConvertibleTo(Json::stringValue)) { container_info.m_name = name.asString(); } const Json::Value& image = container["image"]; if(!image.isNull() && image.isConvertibleTo(Json::stringValue)) { container_info.m_image = image.asString(); } const Json::Value& imageid = container["imageid"]; if(!imageid.isNull() && imageid.isConvertibleTo(Json::stringValue)) { container_info.m_imageid = imageid.asString(); } const Json::Value& privileged = container["privileged"]; if(!privileged.isNull() && privileged.isConvertibleTo(Json::booleanValue)) { container_info.m_privileged = privileged.asBool(); } sinsp_container_info::parse_json_mounts(container["Mounts"], container_info.m_mounts); const Json::Value& contip = container["ip"]; if(!contip.isNull() && contip.isConvertibleTo(Json::stringValue)) { 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& mesos_task_id = container["mesos_task_id"]; if(!mesos_task_id.isNull() && mesos_task_id.isConvertibleTo(Json::stringValue)) { container_info.m_mesos_task_id = mesos_task_id.asString(); } m_inspector->m_container_manager.add_container(container_info); /* g_logger.log("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, sinsp_logger::SEV_DEBUG); */ } else { throw sinsp_exception("Invalid JSON encountered while parsing container info: " + json); } } 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); } void sinsp_parser::parse_cpu_hotplug_enter(sinsp_evt *evt) { #ifdef HAS_ANALYZER if(m_inspector->is_live()) { throw sinsp_exception("CPUs configuration change detected. Aborting."); } #endif } 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; } } int sinsp_parser::get_k8s_version(const std::string& json) { if(m_k8s_capture_version == k8s_state_t::CAPTURE_VERSION_NONE) { g_logger.log(json, sinsp_logger::SEV_DEBUG); Json::Value root; if(Json::Reader().parse(json, root)) { const Json::Value& items = root["items"]; // new if(!items.isNull()) { g_logger.log("K8s capture version " + std::to_string(k8s_state_t::CAPTURE_VERSION_2) + " detected.", sinsp_logger::SEV_DEBUG); 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()) { g_logger.log("K8s capture version " + std::to_string(k8s_state_t::CAPTURE_VERSION_2) + " detected.", sinsp_logger::SEV_DEBUG); m_k8s_capture_version = k8s_state_t::CAPTURE_VERSION_1; return m_k8s_capture_version; } throw sinsp_exception("Unrecognized K8s capture format."); } else { throw sinsp_exception("Invalid K8s capture JSON encountered."); } } 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); //g_logger.log(json, sinsp_logger::SEV_DEBUG); 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); //g_logger.log(json, sinsp_logger::SEV_DEBUG); ASSERT(m_inspector); ASSERT(m_inspector->m_mesos_client); m_inspector->m_mesos_client->simulate_event(json); } 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) { evt->get_thread_info()->m_sid = retval; } } 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.19.1/userspace/libsinsp/parsers.h000066400000000000000000000140071316537151600205630ustar00rootroot00000000000000/* 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 . */ //////////////////////////////////////////////////////////////////////////// // Public definitions for the scap library //////////////////////////////////////////////////////////////////////////// #pragma once 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); void parse_k8s_evt(sinsp_evt *evt); void parse_chroot_exit(sinsp_evt *evt); void parse_mesos_evt(sinsp_evt *evt); void parse_setsid_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 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); // 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); // Return false if the update didn't happen because the tuple is identical to the given address bool set_ipv4_mapped_ipv6_addresses_and_ports(sinsp_fdinfo_t* fdinfo, uint8_t* packed_data); // Return false if the update didn't happen because the tuple is identical to the given address bool set_unix_info(sinsp_fdinfo_t* fdinfo, uint8_t* packed_data); void swap_ipv4_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; // 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.19.1/userspace/libsinsp/prefix_search.cpp000066400000000000000000000112361316537151600222620ustar00rootroot00000000000000/* Copyright (C) 2013-2016 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 #include "prefix_search.h" using namespace std; path_prefix_search::path_prefix_search() { } path_prefix_search::~path_prefix_search() { for (auto &ent : m_dirs) { delete(ent.second); } } // Split path /var/log/messages into dirent (var) and remainder (/log/messages) void path_prefix_search::split_path(const filter_value_t &path, filter_value_t &dirent, filter_value_t &remainder) { uint32_t length = path.second; if(path.second == 0) { // The result of splitting an empty string is 2 empty strings return; } // Skip any trailing /, not needed if (path.first[path.second-1] == '/') { length--; } uint32_t start = 0; // Also skip any leading '/', not needed. if(path.first[0] == '/') { start++; } uint8_t* pos = path.first + start; uint32_t counter = 0; while(counter < path.second) { if (*pos == 0x2F) // '/' { break; } ++pos; if(++counter >= path.second) { pos = NULL; break; } } if(pos == NULL || pos >= (path.first + length)) { dirent.first = path.first + start; dirent.second = length-start; } else { dirent.first = path.first + start; dirent.second = (uint8_t *) pos-dirent.first; remainder.first = (uint8_t *) pos; remainder.second = length-dirent.second-start; } } // NOTE: this does not copy, so it is only valid as long as path is valid. void path_prefix_search::add_search_path(const char *path) { filter_value_t mem((uint8_t *) path, (uint32_t) strlen(path)); return add_search_path(mem); } void path_prefix_search::add_search_path(const filter_value_t &path) { filter_value_t dirent, remainder; path_prefix_search *subtree = NULL; path_prefix_search::split_path(path, dirent, remainder); auto it = m_dirs.find(dirent); if(it == m_dirs.end()) { // This path component doesn't match any existing // dirent. We need to add one and its subtree. if(remainder.second > 0) { subtree = new path_prefix_search(); subtree->add_search_path(remainder); } m_dirs[dirent] = subtree; } 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(remainder.second == 0) { // 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); m_dirs.erase(dirent); m_dirs[dirent] = NULL; } else if(it->second == 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->add_search_path(remainder); } } } // NOTE: this does not copy, so it is only valid as long as path is valid. bool path_prefix_search::match(const char *path) { filter_value_t mem((uint8_t *) path, (uint32_t) strlen(path)); return match(mem); } bool path_prefix_search::match(const filter_value_t &path) { filter_value_t dirent, remainder; path_prefix_search::split_path(path, dirent, remainder); auto it = m_dirs.find(dirent); if(it == m_dirs.end()) { return false; } 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(remainder.second == 0) { return (it->second == NULL); } else if(it->second == NULL) { // /foo/bar matched a prefix /foo, so we're // done. return true; } else { return it->second->match(remainder); } } } std::string path_prefix_search::as_string() { return as_string(string("")); } // Unlike all the other methods, this does perform copies. std::string path_prefix_search::as_string(const std::string &prefix) { std::ostringstream os; for (auto &it : m_dirs) { string dirent((const char *) it.first.first, it.first.second); os << prefix << dirent << " -> " << endl; if(it.second) { std::string indent = prefix; indent += " "; os << it.second->as_string(indent); } } return os.str(); } sysdig-0.19.1/userspace/libsinsp/prefix_search.h000066400000000000000000000051431316537151600217270ustar00rootroot00000000000000/* Copyright (C) 2013-2016 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 #include #include "filter_value.h" // // 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. class path_prefix_search { public: path_prefix_search(); ~path_prefix_search(); void add_search_path(const char *path); void add_search_path(const filter_value_t &path); bool match(const char *path); bool match(const filter_value_t &path); std::string as_string(); private: std::string as_string(const std::string &prefix); static void split_path(const filter_value_t &path, filter_value_t &dirent, filter_value_t &remainder); // 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_search(/run) // - (etc, NULL) // - (lib, NULL) // - (usr, NULL) // - (var, path_prefix_search(/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_search object. std::unordered_map m_dirs; }; sysdig-0.19.1/userspace/libsinsp/procinfo_test.cpp000066400000000000000000000100521316537151600223110ustar00rootroot00000000000000/* 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 "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.19.1/userspace/libsinsp/protodecoder.cpp000066400000000000000000000154271316537151600221370ustar00rootroot00000000000000/* 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 #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.19.1/userspace/libsinsp/protodecoder.h000066400000000000000000000076711316537151600216060ustar00rootroot00000000000000/* 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 . */ #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.19.1/userspace/libsinsp/settings.h000066400000000000000000000060301316537151600207410ustar00rootroot00000000000000/* 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 . */ #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 32768 // // 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 sacnned for inactive threads // #define DEFAULT_INACTIVE_THREAD_SCAN_TIME_S 1200 // // How often the thread table is sacnned for inactive threads // #define DEFAULT_INACTIVE_CONTAINER_SCAN_TIME_S DEFAULT_INACTIVE_THREAD_SCAN_TIME_S // // 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 // // 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.19.1/userspace/libsinsp/sinsp.cpp000066400000000000000000001367211316537151600206030ustar00rootroot00000000000000/* 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 #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 "k8s_api_handler.h" #ifdef HAS_ANALYZER #include "analyzer_int.h" #include "analyzer.h" #endif extern sinsp_evttables g_infotables; #ifdef HAS_CHISELS extern vector* g_chisel_dirs; #endif void on_new_entry_from_proc(void* context, int64_t tid, scap_threadinfo* tinfo, scap_fdinfo* fdinfo, scap_t* newhandle); /////////////////////////////////////////////////////////////////////////////// // sinsp implementation /////////////////////////////////////////////////////////////////////////////// sinsp::sinsp() : m_evt(this), m_container_manager(this) { 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 = MAX_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_max_n_proc_lookups = 0; m_max_n_proc_socket_lookups = 0; m_snaplen = DEFAULT_SNAPLEN; m_buffer_format = sinsp_evt::PF_NORMAL; m_input_fd = 0; 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_meta_evt_buf = new char[SP_EVT_BUF_SIZE]; m_meta_evt.m_pevt = (scap_evt*) m_meta_evt_buf; m_meta_evt_pending = false; m_meta_skipped_evt_res = 0; m_meta_skipped_evt = NULL; 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; // 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; 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; 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; 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_meta_evt_buf) { delete[] m_meta_evt_buf; m_meta_evt_buf = NULL; } if(m_meinfo.m_piscapevt) { delete[] m_meinfo.m_piscapevt; } delete m_k8s_client; delete m_k8s_api_server; delete m_k8s_api_cert; delete m_mesos_client; } 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) 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) 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; } } // // 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 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; m_h = scap_open(oargs, error); if(m_h == NULL) { throw sinsp_exception(error); } 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; m_h = scap_open(oargs, error); if(m_h == NULL) { throw sinsp_exception(error); } 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; } m_h = scap_open(oargs, error); if(m_h == NULL) { throw sinsp_exception(error); } 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(string filename) { if(filename == "") { 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); } else { m_dumper = scap_dump_open(m_h, dump_filename.c_str(), SCAP_COMPRESSION_NONE); } 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, int64_t tid, scap_threadinfo* tinfo, scap_fdinfo* fdinfo, scap_t* newhandle) { ASSERT(tinfo != NULL); // // Retrieve machine information if we don't have it yet //proc { m_machine_info = scap_get_machine_info(newhandle); 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) { sinsp_threadinfo newti(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) { m_thread_manager->add_thread(newti, true); } } else { m_thread_manager->add_thread(newti, true); } } else { sinsp_threadinfo* sinsp_tinfo = find_thread(tid, true); if(sinsp_tinfo == NULL) { sinsp_threadinfo newti(this); newti.init(tinfo); m_thread_manager->add_thread(newti, true); sinsp_tinfo = find_thread(tid, true); if(sinsp_tinfo == NULL) { 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, int64_t tid, scap_threadinfo* tinfo, scap_fdinfo* fdinfo, scap_t* newhandle) { sinsp* _this = (sinsp*)context; _this->on_new_entry_from_proc(context, tid, tinfo, fdinfo, newhandle); } 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(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_meta_evt_pending && m_meta_skipped_evt != NULL) { res = m_meta_skipped_evt_res; evt = m_meta_skipped_evt; m_meta_evt_pending = false; } 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, sinsp_analyzer::DF_TIMEOUT); } #endif *puevt = NULL; return res; } else if(res == SCAP_EOF) { #ifdef HAS_ANALYZER if(m_analyzer) { m_analyzer->process_event(NULL, sinsp_analyzer::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_from_driver(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; } // // 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 // No meta event is pending unless it's set in process_event // below. m_meta_evt_pending = false; // // 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 // A side-effect of parsing this event may have generated a // meta event. For example, parsing an execve or clone into a // new cgroup may have created a container event. // // We want that meta event to be returned/written to files // *before* the original system event. So save the system // event so it can be returned/written in the next call to // sinsp::next() and make the meta event the current event. if(m_meta_evt_pending) { m_meta_evt.m_evtnum = evt->m_evtnum; m_meta_skipped_evt = evt; m_meta_skipped_evt_res = res; res = SCAP_SUCCESS; evt = &m_meta_evt; } // // 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, sinsp_analyzer::DF_FORCE_FLUSH); } else if(sw) { m_analyzer->process_event(evt, sinsp_analyzer::DF_FORCE_FLUSH_BUT_DONT_EMIT); } else { m_analyzer->process_event(evt, sinsp_analyzer::DF_FORCE_NOFLUSH); } } #else // SIMULATE_DROP_MODE m_analyzer->process_event(evt, sinsp_analyzer::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) { return find_thread(tid, lookup_only); } sinsp_threadinfo* sinsp::get_thread(int64_t tid, bool query_os_if_not_found, bool lookup_only) { sinsp_threadinfo* sinsp_proc = find_thread(tid, lookup_only); if(sinsp_proc == NULL && 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 )) { scap_threadinfo* scap_proc = NULL; sinsp_threadinfo newti(this); m_n_proc_lookups++; 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, tid, m_n_proc_lookups_duration_ns / 1000000); } if(m_n_proc_lookups == m_max_n_proc_lookups) { g_logger.format(sinsp_logger::SEV_INFO, "Reached max process lookup number, duration=%" PRIu64, m_n_proc_lookups_duration_ns / 1000000); } if(m_max_n_proc_lookups == 0 || (m_max_n_proc_lookups != 0 && (m_n_proc_lookups <= m_max_n_proc_lookups))) { bool scan_sockets = false; if(m_max_n_proc_socket_lookups == 0 || (m_max_n_proc_socket_lookups != 0 && (m_n_proc_lookups <= m_max_n_proc_socket_lookups))) { scan_sockets = true; } #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; } // // Since this thread is created out of thin air, we need to // properly set its reference count, by scanning the table // threadinfo_map_t* pttable = &m_thread_manager->m_threadtable; threadinfo_map_iterator_t it; for(it = pttable->begin(); it != pttable->end(); ++it) { if(it->second.m_pid == tid) { newti.m_nchilds++; } } // // 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); } void sinsp::add_thread(const sinsp_threadinfo& ptinfo) { 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); } 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::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 &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, 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; } 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)); } } #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; strcpy(ncdi.m_dir, dirname.c_str()); 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_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(); } 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(string* api_server, string* ssl_cert) { #ifdef HAS_CAPTURE if(ssl_cert && (!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); ssl_cert->clear(); } else { while(ssl_cert->length()) { if(cert.empty() && pos != std::string::npos) { cert = ssl_cert->substr(0, pos); if(ssl_cert->length() > (pos + 1)) { *ssl_cert = ssl_cert->substr(pos + 1); } else { break; } } else if(key.empty()) { key = ssl_cert->substr(0, pos); if(ssl_cert->length() > (pos + 1)) { *ssl_cert = ssl_cert->substr(pos + 1); std::string::size_type s_pos = key.find('#'); if(s_pos != std::string::npos && key.length() > (s_pos + 1)) { key_pwd = key.substr(s_pos + 1); key = key.substr(0, s_pos); } if(pos == std::string::npos) { break; } } else { break; } } else if(ca_cert.empty()) { ca_cert = *ssl_cert; ssl_cert->clear(); } else { goto ssl_err; } pos = ssl_cert->find(':', pos); } if(cert.empty() || key.empty()) { goto ssl_err; } } m_k8s_ssl = std::make_shared(cert, key, key_pwd, ca_cert, ca_cert.empty() ? false : true, "PEM"); } return; ssl_err: throw sinsp_exception(string("Invalid K8S SSL entry: ") + (ssl_cert ? *ssl_cert : string("NULL"))); #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(api_server, 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_server, 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_server, 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); } } } /////////////////////////////////////////////////////////////////////////////// // 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) { 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. // for(threadinfo_map_iterator_t it = m_threadtable.begin(); it != m_threadtable.end();) { bool closed = (it->second.m_flags & PPM_CL_CLOSED) != 0; if(closed || ((m_inspector->m_lastevent_ts > it->second.m_lastaccess_ts + m_inspector->m_thread_timeout_ns) && !scap_is_thread_alive(m_inspector->m_h, it->second.m_pid, it->first, it->second.m_comm.c_str())) ) { // // Reset the cache // m_last_tid = 0; m_last_tinfo = NULL; #ifdef GATHER_INTERNAL_STATS m_removed_threads->increment(); #endif remove_thread(it++, closed); } else { ++it; } } // // 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.19.1/userspace/libsinsp/sinsp.h000066400000000000000000000713511316537151600202450ustar00rootroot00000000000000/* 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 . */ /*! \mainpage libsinsp documentation \section Introduction libsinsp is a system inspection library written in C++ and implementing high level functionlity 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 #ifdef _WIN32 #pragma warning(disable: 4251 4200 4221 4190) #endif #ifdef _WIN32 #define SINSP_PUBLIC __declspec(dllexport) #include #else #define SINSP_PUBLIC #include #endif #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 #define VISIBILITY_PRIVATE private: #endif #define ONE_SECOND_IN_NS 1000000000LL // // 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; #include "tuples.h" #include "fdinfo.h" #include "threadinfo.h" #include "ifinfo.h" #include "eventformatter.h" class sinsp_partial_transaction; class sinsp_parser; class sinsp_analyzer; class sinsp_filter; class cycle_writer; class sinsp_protodecoder; class k8s; 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 vector sinsp_split(const string &s, char delim); /*! \brief Information about a chisel */ class sinsp_chisel_details { public: string m_name; 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; } char const* what() const throw() { return m_error_str.c_str(); } string m_error_str; }; /*! \brief sinsp library exception. */ struct sinsp_capture_interrupt_exception : sinsp_exception { }; /*! \brief The deafult 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: typedef std::set k8s_ext_list_t; typedef std::shared_ptr k8s_ext_list_ptr_t; sinsp(); ~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. */ 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(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 succesful 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 next() */ 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 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. \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 &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 tracefile 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(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); /*! \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 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); #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 iterface 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 current capture is live */ inline bool is_nodriver() { return m_mode == SCAP_MODE_NODRIVER; } /*! \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 avent 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 arugment 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(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. */ 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 succesful 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 succesful 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(); void init_k8s_ssl(string* api_server, string* ssl_cert); void init_k8s_client(string* api_server, string* ssl_cert, bool verbose = false); void make_k8s_client(); k8s* get_k8s_client() const { return m_k8s_client; } void init_mesos_client(string* api_server, bool verbose = false); mesos* get_mesos_client() const { return m_mesos_client; } // // Misc internal stuff // void stop_dropping_mode(); void start_dropping_mode(uint32_t sampling_ratio); void on_new_entry_from_proc(void* context, int64_t tid, scap_threadinfo* tinfo, scap_fdinfo* fdinfo, scap_t* newhandle); 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(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(); vector get_n_tracepoint_hit(); static unsigned num_possible_cpus(); VISIBILITY_PRIVATE // 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 add_thread(const sinsp_threadinfo& ptinfo); 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 sinsp_threadinfo* find_thread(int64_t tid, bool lookup_only) { threadinfo_map_iterator_t it; // // Try looking up in our simple cache // if(m_thread_manager->m_last_tinfo && tid == m_thread_manager->m_last_tid) { #ifdef GATHER_INTERNAL_STATS m_thread_manager->m_cached_lookups->increment(); #endif m_thread_manager->m_last_tinfo->m_lastaccess_ts = m_lastevent_ts; return m_thread_manager->m_last_tinfo; } // // Caching failed, do a real lookup // it = m_thread_manager->m_threadtable.find(tid); if(it != m_thread_manager->m_threadtable.end()) { #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 = &(it->second); m_thread_manager->m_last_tinfo->m_lastaccess_ts = m_lastevent_ts; } return &(it->second); } 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(); void k8s_discover_ext(); void collect_k8s(); void update_k8s_state(); void update_mesos_state(); bool get_mesos_data(); 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); } scap_t* m_h; uint32_t m_nevts; int64_t m_filesize; scap_mode_t m_mode; // If non-zero, reading from this fd and m_input_filename contains "fd // ". Otherwise, reading from m_input_filename. int m_input_fd; string m_input_filename; 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; string m_lasterr; int64_t m_tid_to_remove; int64_t m_tid_of_fd_to_remove; 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; sinsp_network_interfaces* m_network_interfaces; public: sinsp_thread_manager* m_thread_manager; sinsp_container_manager m_container_manager; // // Kubernetes // string* m_k8s_api_server; 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; // // Mesos/Marathon // string m_mesos_api_server; 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 deafult 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; string m_filterstring; #endif // // Internal stats // #ifdef GATHER_INTERNAL_STATS sinsp_stats m_stats; #endif uint32_t m_n_proc_lookups; uint64_t m_n_proc_lookups_duration_ns; uint32_t m_max_n_proc_lookups; uint32_t m_max_n_proc_socket_lookups; #ifdef HAS_ANALYZER vector m_tid_collisions; #endif // // Saved snaplen // uint32_t m_snaplen; // // 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 // vector m_decoders_reset_list; // // Containers meta event management // sinsp_evt m_meta_evt; // XXX this should go away char* m_meta_evt_buf; // XXX this should go away bool m_meta_evt_pending; // XXX this should go away int32_t m_meta_skipped_evt_res; sinsp_evt* m_meta_skipped_evt; // // 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; // // 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; static unsigned int m_num_possible_cpus; #if defined(HAS_CAPTURE) int64_t m_sysdig_pid; #endif 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 k8s_delegator; #ifdef HAS_ANALYZER friend class thread_analyzer_info; #endif template friend class sinsp_connection_manager; }; /*@}*/ sysdig-0.19.1/userspace/libsinsp/sinsp.vcxproj000066400000000000000000000163251316537151600215110ustar00rootroot00000000000000 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.19.1/userspace/libsinsp/sinsp.vcxproj.filters000066400000000000000000000100121316537151600231430ustar00rootroot00000000000000 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.19.1/userspace/libsinsp/sinsp_auth.cpp000066400000000000000000000063741316537151600216240ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/sinsp_auth.h000066400000000000000000000041461316537151600212640ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/sinsp_curl.cpp000066400000000000000000000275351316537151600216320ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/sinsp_curl.h000066400000000000000000000074111316537151600212660ustar00rootroot00000000000000// // 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; } const 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.19.1/userspace/libsinsp/sinsp_errno.h000066400000000000000000000226501316537151600214500ustar00rootroot00000000000000/* 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 . */ #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.19.1/userspace/libsinsp/sinsp_int.h000066400000000000000000000074621316537151600211210ustar00rootroot00000000000000/* 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 . */ //////////////////////////////////////////////////////////////////////////// // 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" #ifndef MIN #define MIN(X,Y) ((X) < (Y)? (X):(Y)) #define MAX(X,Y) ((X) > (Y)? (X):(Y)) #endif // // ASSERT implementation // #ifdef _DEBUG #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 SINSP_PUBLIC __declspec(dllexport) #define BRK(X) {if(evt != NULL && evt->get_num() == X)__debugbreak();} #else #define SINSP_PUBLIC #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 void on_new_container(const sinsp_container_info& container_info) = 0; virtual void on_remove_container(const sinsp_container_info& container_info) = 0; }; sysdig-0.19.1/userspace/libsinsp/sinsp_signal.h000066400000000000000000000031751316537151600216010ustar00rootroot00000000000000/* 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 . */ #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.19.1/userspace/libsinsp/sinsp_test.cpp000066400000000000000000000020441316537151600216300ustar00rootroot00000000000000/* 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 "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.19.1/userspace/libsinsp/socket_collector.h000066400000000000000000000201171316537151600224410ustar00rootroot00000000000000// // 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: atempt 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.19.1/userspace/libsinsp/socket_handler.h000066400000000000000000001411051316537151600220710ustar00rootroot00000000000000// // 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 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 = (struct gaicb**)calloc(1, sizeof(struct gaicb*)); m_dns_reqs[0] = (struct gaicb*)calloc(1, sizeof(struct gaicb)); m_dns_reqs[0]->ar_name = strdup(m_url.get_host().c_str()); ret = getaddrinfo_a(GAI_NOWAIT, &m_dns_reqs[0], 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_cleanup(struct gaicb** dns_reqs) { 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) { if(dns_reqs[0]->ar_result) { freeaddrinfo(dns_reqs[0]->ar_result); } if(dns_reqs[0]->ar_name) { free((void*)dns_reqs[0]->ar_name); } free(dns_reqs[0]); dns_reqs[0] = 0; free(dns_reqs); 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_cleanup(*it)) { 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_cleanup(m_dns_reqs)) { m_dns_reqs = 0; } else // store for postponed canceling { m_pending_dns_reqs.push_back(m_dns_reqs); } } 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); } typedef std::deque dns_list_t; 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; struct gaicb** 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.19.1/userspace/libsinsp/stats.cpp000066400000000000000000000051171316537151600205770ustar00rootroot00000000000000/* 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 . */ //////////////////////////////////////////////////////////////////////////// // 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.19.1/userspace/libsinsp/stats.h000066400000000000000000000027771316537151600202550ustar00rootroot00000000000000/* 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 . */ #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.19.1/userspace/libsinsp/stopwatch.cpp000066400000000000000000000001711316537151600214500ustar00rootroot00000000000000// // stopwatch.cpp // // stopwatch utility // #include "stopwatch.h" sinsp_stopwatch::sinsp_stopwatch() { start(); } sysdig-0.19.1/userspace/libsinsp/stopwatch.h000066400000000000000000000016471316537151600211260ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/table.cpp000066400000000000000000000767411316537151600205430ustar00rootroot00000000000000/* 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 #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 spefied, 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 appen 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; 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; for(auto it = threadtable->begin(); it != threadtable->end(); ++it) { tevt.m_tinfo = &it->second; tscapevt.tid = tevt.m_tinfo->m_tid; if(m_filter) { if(!m_filter->run(&tevt)) { continue; } } process_event(&tevt); } } 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_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_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_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_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: 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.19.1/userspace/libsinsp/table.h000066400000000000000000000177501316537151600202030ustar00rootroot00000000000000/* 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 . */ #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.19.1/userspace/libsinsp/third-party/000077500000000000000000000000001316537151600212005ustar00rootroot00000000000000sysdig-0.19.1/userspace/libsinsp/third-party/jsoncpp/000077500000000000000000000000001316537151600226545ustar00rootroot00000000000000sysdig-0.19.1/userspace/libsinsp/third-party/jsoncpp/json/000077500000000000000000000000001316537151600236255ustar00rootroot00000000000000sysdig-0.19.1/userspace/libsinsp/third-party/jsoncpp/json/json-forwards.h000066400000000000000000000214031316537151600265740ustar00rootroot00000000000000/// 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.19.1/userspace/libsinsp/third-party/jsoncpp/json/json.h000066400000000000000000001732051316537151600247570ustar00rootroot00000000000000/// 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 is generated by CMake from "version" // and "version.h.in" files. // Run CMake configure step to update it. #ifndef JSON_VERSION_H_INCLUDED # define JSON_VERSION_H_INCLUDED # define JSONCPP_VERSION_STRING "0.10.2" # define JSONCPP_VERSION_MAJOR 0 # define JSONCPP_VERSION_MINOR 10 # define JSONCPP_VERSION_PATCH 2 # 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) /** \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; /** 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; /** 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; /// used internally void throwRuntimeError(std::string const& msg); /// used internally 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* beginValue, const char* endValue); ///< 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** str, 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 /// \param key may contain embedded nulls. Value get(const char* key, 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-key) >= 2^30 Value const* find(char const* key, char const* end) const; /// Most general and efficient version of object-mutators. /// \note As stated elsewhere, behavior is undefined if (end-key) >= 2^30 /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. Value const* demand(char const* key, 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* key, 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* key, 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* key, 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. 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. 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.19.1/userspace/libsinsp/third-party/jsoncpp/jsoncpp.cpp000066400000000000000000004226671316537151600250550ustar00rootroot00000000000000/// 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] = 0x80 | static_cast((0x3f & (cp >> 6))); result[0] = 0xE0 | static_cast((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 [0,32[). 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 = char(value % 10) + '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 #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below #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 static int const stackLimit_g = 1000; static int stackDepth_g = 0; // see readValue() namespace Json { typedef std::auto_ptr CharReaderPtr; // 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::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 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; const int bufferSize = 32; int count; int length = int(token.end_ - token.start_); // Sanity check to avoid buffer overflow exploits. if (length < 0) { return addError("Unable to parse token length", token); } // Avoid using a string constant for the format control string given to // sscanf, as this can cause hard to debug crashes on OS X. See here for more // info: // // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html char format[] = "%lf"; if (length <= bufferSize) { Char buffer[bufferSize + 1]; memcpy(buffer, token.start_, length); buffer[length] = 0; count = sscanf(buffer, format, &value); } else { std::string buffer(token.start_, token.end_); count = sscanf(buffer.c_str(), format, &value); } if (count != 1) 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]; #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) #if defined(WINCE) _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); #else sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column); #endif #else snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); #endif 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_; int stackLimit_; }; // OurFeatures // exact copy of Implementation of class Features // //////////////////////////////// OurFeatures::OurFeatures() : allowComments_(true), strictRoot_(false) , allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) , allowSingleQuotes_(false) , failIfExtra_(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, 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(); 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_; 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 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': 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 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; } void OurReader::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 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; const int bufferSize = 32; int count; int length = int(token.end_ - token.start_); // Sanity check to avoid buffer overflow exploits. if (length < 0) { return addError("Unable to parse token length", token); } // Avoid using a string constant for the format control string given to // sscanf, as this can cause hard to debug crashes on OS X. See here for more // info: // // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html char format[] = "%lf"; if (length <= bufferSize) { Char buffer[bufferSize + 1]; memcpy(buffer, token.start_, length); buffer[length] = 0; count = sscanf(buffer, format, &value); } else { std::string buffer(token.start_, token.end_); count = sscanf(buffer.c_str(), format, &value); } if (count != 1) 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]; #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) #if defined(WINCE) _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); #else sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column); #endif #else snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); #endif 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(); 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"); } 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; //! [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; //! [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* key; char const* end; key = memberName(&end); if (!key) return std::string(); return std::string(key, end); } char const* ValueIteratorBase::memberName() const { const char* name = (*current_).first.data(); return name ? name : ""; } char const* ValueIteratorBase::memberName(char const** end) const { const char* name = (*current_).first.data(); if (!name) { *end = NULL; return NULL; } *end = name + (*current_).first.length(); return name; } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // 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 + 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 = 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 { 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_; }; class JSON_API RuntimeError : public Exception { public: RuntimeError(std::string const& msg); }; class JSON_API LogicError : public Exception { public: LogicError(std::string const& msg); }; 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) {} void throwRuntimeError(std::string const& msg) { throw RuntimeError(msg); } 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 index) : cstr_(0), index_(index) {} Value::CZString::CZString(char const* str, unsigned length, DuplicationPolicy allocate) : cstr_(str) { // allocate != duplicate storage_.policy_ = allocate; storage_.length_ = length; } 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_ ? (other.storage_.policy_ == noDuplication ? noDuplication : duplicate) : 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 type) { initBasic(type); switch (type) { 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; } 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** end) const { if (type_ != stringValue) return false; if (value_.string_ == 0) return false; unsigned length; decodePrefixedString(this->allocated_, this->value_.string_, &length, str); *end = *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: return value_.real_ ? 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 type, bool allocated) { type_ = type; 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* end) { 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(end-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* end) 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(end-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* end, Value const& defaultValue) const { Value const* found = find(key, end); 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* end, Value* removed) { if (type_ != objectValue) { return false; } CZString actualKey(key, static_cast(end-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 key(i); (*value_.map_)[key] = (*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* end) const { Value const* value = find(key, end); 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) && _MSC_VER < 1500 // VC++ 8.0 and below #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 { typedef std::auto_ptr StreamWriterPtr; 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); bool isNegative = value < 0; if (isNegative) value = -value; uintToString(LargestUInt(value), current); if (isNegative) *--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) { // Allocate a buffer that is more than large enough to store the 16 digits of // precision requested below. char buffer[32]; int len = -1; // 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 defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with // visual studio 2005 to // avoid warning. #if defined(WINCE) len = _snprintf(buffer, sizeof(buffer), "%.17g", value); #else len = sprintf_s(buffer, sizeof(buffer), "%.17g", value); #endif #else if (isfinite(value)) { len = snprintf(buffer, sizeof(buffer), "%.17g", value); } else { // IEEE standard states that NaN values will not compare to themselves if (value != value) { len = snprintf(buffer, sizeof(buffer), "null"); } else if (value < 0) { len = snprintf(buffer, sizeof(buffer), "-1e+9999"); } else { len = snprintf(buffer, sizeof(buffer), "1e+9999"); } // For those, we do not need to call fixNumLoc, but it is fast. } #endif assert(len >= 0); fixNumericLocale(buffer, buffer + len); return buffer; } 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(), 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); 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; }; BuiltStyledStreamWriter::BuiltStyledStreamWriter( std::string const& indentation, CommentStyle::Enum cs, std::string const& colonSymbol, std::string const& nullSymbol, std::string const& endingLineFeedSymbol) : rightMargin_(74) , indentation_(indentation) , cs_(cs) , colonSymbol_(colonSymbol) , nullSymbol_(nullSymbol) , endingLineFeedSymbol_(endingLineFeedSymbol) , addChildValues_(false) , indented_(false) { } 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())); 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(), 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(); 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 = ""; } std::string endingLineFeedSymbol = ""; return new BuiltStyledStreamWriter( indentation, cs, colonSymbol, nullSymbol, endingLineFeedSymbol); } 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"); } 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; //! [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.19.1/userspace/libsinsp/third-party/tinydir.h000066400000000000000000000206671316537151600230460ustar00rootroot00000000000000/* 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.19.1/userspace/libsinsp/threadinfo.cpp000066400000000000000000001111731316537151600215640ustar00rootroot00000000000000/* 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 . */ #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; 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 = NULL; 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_blprogram = NULL; } 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 void sinsp_threadinfo::compute_program_hash() { string phs = m_exe; phs += m_container_id; // // By default, the falco hash is just exe+container // m_program_hash_falco = std::hash()(phs); // // The program hash includes the arguments as well // for(auto arg = m_args.begin(); arg != m_args.end(); ++arg) { phs += *arg; } m_program_hash = std::hash()(phs); // // 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(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(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, fdi->info.ipv6info.sip); copy_ipv6_address(newfdi->m_sockinfo.m_ipv6info.m_fields.m_dip, 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; 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, 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; 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_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; 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; 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) { 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; } } 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 ASSERT(false); return m_env; } } } string sinsp_threadinfo::get_env(const string& name) { for(const auto& env_var : get_env()) { if((env_var.length() > name.length()) && (env_var.substr(0, name.length()) == name)) { std::string::size_type pos = env_var.find('='); if(pos != std::string::npos && env_var.size() > pos + 1) { string val = env_var.substr(pos + 1); std::string::size_type first = val.find_first_not_of(' '); std::string::size_type last = val.find_last_not_of(' '); return val.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 = strchr(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); while(slow) { 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, there's a loop // in the thread state. if(slow && (slow == fast)) { // 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), sinsp_logger::SEV_WARNING); m_parent_loop_detected = true; } return; } } } } sinsp_threadinfo* sinsp_threadinfo::lookup_thread() { return m_inspector->get_thread(m_pid, true, true); } // // Note: this is duplicated here because visual studio has trouble inlining // the method. // #ifdef _WIN32 sinsp_threadinfo* sinsp_threadinfo::get_main_thread() { if (m_main_thread == NULL) { // // 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. // sinsp_threadinfo* ptinfo = lookup_thread(); if (NULL == ptinfo) { return NULL; } m_main_thread = ptinfo; } } return m_main_thread; } #endif inline void scpy(char* dst, const char* src, uint32_t maxlen) { if(maxlen == 0) { return; } while(true) { if(maxlen == 1) { *dst = 0; return; } if(*src == 0) { *dst = 0; return; } *(dst++) = *(src++); maxlen--; } } void sinsp_threadinfo::args_to_scap(scap_threadinfo* sctinfo) { uint32_t alen = SCAP_MAX_ARGS_SIZE; uint32_t tlen = 0; char* dst = sctinfo->args; for(auto it = m_args.begin(); it != m_args.end(); ++it) { uint32_t len = it->size() + 1; scpy(dst + tlen, it->c_str(), alen); if(len >= alen) { // // We saturated the args buffer. Null terminate it and return // sctinfo->args[SCAP_MAX_ARGS_SIZE - 1] = 0; sctinfo->args_len = SCAP_MAX_ARGS_SIZE; return; } else { tlen += len; alen -= len; } } sctinfo->args_len = tlen; } void sinsp_threadinfo::env_to_scap(scap_threadinfo* sctinfo) { uint32_t alen = SCAP_MAX_ENV_SIZE; uint32_t tlen = 0; char* dst = sctinfo->env; for(auto it = m_env.begin(); it != m_env.end(); ++it) { uint32_t len = it->size() + 1; scpy(dst + tlen, it->c_str(), alen); if(len >= alen) { // // We saturated the args buffer. Null terminate it and return // sctinfo->env[SCAP_MAX_ENV_SIZE - 1] = 0; sctinfo->env_len = SCAP_MAX_ENV_SIZE; return; } else { tlen += len; alen -= len; } } sctinfo->env_len = tlen; } void sinsp_threadinfo::cgroups_to_scap(scap_threadinfo* sctinfo) { uint32_t alen = SCAP_MAX_CGROUPS_SIZE; uint32_t tlen = 0; char* dst = sctinfo->cgroups; for(auto it = m_cgroups.begin(); it != m_cgroups.end(); ++it) { string a = it->first + "=" + it->second; uint32_t len = a.size() + 1; scpy(dst + tlen, a.c_str(), alen); if(len >= alen) { // // We saturated the args buffer. Null terminate it and return // sctinfo->cgroups[SCAP_MAX_CGROUPS_SIZE - 1] = 0; sctinfo->cgroups_len = SCAP_MAX_CGROUPS_SIZE; return; } else { tlen += len; alen -= len; } } sctinfo->cgroups_len = tlen; } 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); copy_ipv6_address(dst->info.ipv6info.dip, src->m_sockinfo.m_ipv6info.m_fields.m_dip); 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); 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); 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 = NULL; 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); } } } void sinsp_thread_manager::add_thread(sinsp_threadinfo& threadinfo, bool from_scap_proctable) { #ifdef GATHER_INTERNAL_STATS m_added_threads->increment(); #endif m_last_tinfo = NULL; if (m_threadtable.size() >= m_inspector->m_max_thread_table_size #if defined(HAS_CAPTURE) && threadinfo.m_pid != m_inspector->m_sysdig_pid #endif ) { m_n_drops++; return; } if(!from_scap_proctable) { increment_mainthread_childcount(&threadinfo); } threadinfo.compute_program_hash(); sinsp_threadinfo& newentry = (m_threadtable[threadinfo.m_tid] = threadinfo); newentry.allocate_private_state(); if(m_listener) { m_listener->on_thread_created(&newentry); } } void sinsp_thread_manager::remove_thread(int64_t tid, bool force) { remove_thread(m_threadtable.find(tid), force); } void sinsp_thread_manager::remove_thread(threadinfo_map_iterator_t it, bool force) { uint64_t nchilds; if(it == m_threadtable.end()) { // // 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 = it->second.m_nchilds) == 0 || force) { // // Decrement the refcount of the main thread/program because // this reference is gone // if(it->second.m_flags & PPM_CL_CLONE_THREAD) { ASSERT(it->second.m_pid != it->second.m_tid); sinsp_threadinfo* main_thread = m_inspector->get_thread(it->second.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(it->second.m_pid == it->second.m_tid) { unordered_map* fdtable = &(it->second.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 = &(it->second); 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 = NULL; #ifdef GATHER_INTERNAL_STATS m_removed_threads->increment(); #endif m_threadtable.erase(it); // // If the thread has a nonzero refcount, it means that we are forcing the removal // of a main process or program that some childs 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() { threadinfo_map_iterator_t it; for(it = m_threadtable.begin(); it != m_threadtable.end(); ++it) { it->second.fix_sockets_coming_from_proc(); } } void sinsp_thread_manager::clear_thread_pointers(threadinfo_map_iterator_t it) { it->second.m_main_thread = NULL; sinsp_fdtable* fdt = it->second.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() { threadinfo_map_iterator_t it; m_last_tinfo = NULL; m_last_tid = 0; for(it = m_threadtable.begin(); it != m_threadtable.end(); ++it) { it->second.m_nchilds = 0; clear_thread_pointers(it); } } void sinsp_thread_manager::create_child_dependencies() { threadinfo_map_iterator_t it; for(it = m_threadtable.begin(); it != m_threadtable.end(); ++it) { increment_mainthread_childcount(&it->second); } } 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(); } void sinsp_thread_manager::thread_to_scap(sinsp_threadinfo& tinfo, scap_threadinfo* sctinfo) { // // Fill in the thread data // sctinfo->tid = tinfo.m_tid; sctinfo->pid = tinfo.m_pid; sctinfo->ptid = tinfo.m_ptid; sctinfo->sid = tinfo.m_sid; strncpy(sctinfo->comm, tinfo.m_comm.c_str(), SCAP_MAX_PATH_SIZE); strncpy(sctinfo->exe, tinfo.m_exe.c_str(), SCAP_MAX_PATH_SIZE); strncpy(sctinfo->exepath, tinfo.m_exepath.c_str(), SCAP_MAX_PATH_SIZE); tinfo.args_to_scap(sctinfo); tinfo.env_to_scap(sctinfo); string tcwd = (tinfo.m_cwd == "")? "/": tinfo.m_cwd; strncpy(sctinfo->cwd, tcwd.c_str(), SCAP_MAX_PATH_SIZE); 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; tinfo.cgroups_to_scap(sctinfo); strncpy(sctinfo->root, tinfo.m_root.c_str(), SCAP_MAX_PATH_SIZE); sctinfo->filtered_out = false; } void sinsp_thread_manager::dump_threads_to_file(scap_dumper_t* dumper) { // // First pass of the table to calculate the length // uint32_t totlen = 0; for(auto it = m_threadtable.begin(); it != m_threadtable.end(); ++it) { scap_threadinfo *sctinfo; if((sctinfo = scap_proc_alloc(m_inspector->m_h)) == NULL) { throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); } sinsp_threadinfo& tinfo = it->second; tinfo.args_to_scap(sctinfo); tinfo.env_to_scap(sctinfo); tinfo.cgroups_to_scap(sctinfo); string tcwd = (tinfo.m_cwd == "")? "/": tinfo.m_cwd; uint32_t il = (uint32_t) (sizeof(uint64_t) + // tid sizeof(uint64_t) + // pid sizeof(uint64_t) + // ptid sizeof(uint64_t) + // sid 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 + sctinfo->args_len + 2 + MIN(tcwd.size(), SCAP_MAX_PATH_SIZE) + sizeof(uint64_t) + // fdlimit 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 + sctinfo->env_len + sizeof(int64_t) + // vtid sizeof(int64_t) + // vpid 2 + sctinfo->cgroups_len + sizeof(uint32_t) + 2 + MIN(tinfo.m_root.size(), SCAP_MAX_PATH_SIZE)); totlen += il; scap_proc_free(m_inspector->m_h, sctinfo); } // // 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)); } for(auto it = m_threadtable.begin(); it != m_threadtable.end(); ++it) { scap_threadinfo *sctinfo; if((sctinfo = scap_proc_alloc(m_inspector->m_h)) == NULL) { throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); } sinsp_threadinfo& tinfo = it->second; thread_to_scap(tinfo, sctinfo); if(scap_write_proclist_entry(m_inspector->m_h, dumper, sctinfo) != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); } scap_proc_free(m_inspector->m_h, sctinfo); } 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 // for(auto it = m_threadtable.begin(); it != m_threadtable.end(); ++it) { scap_threadinfo *sctinfo; if((sctinfo = scap_proc_alloc(m_inspector->m_h)) == NULL) { throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); } sinsp_threadinfo& tinfo = it->second; 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(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"); } } } // // 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"); } scap_proc_free(m_inspector->m_h, sctinfo); } } sysdig-0.19.1/userspace/libsinsp/threadinfo.h000066400000000000000000000302151316537151600212260ustar00rootroot00000000000000/* 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 . */ #pragma once #ifndef VISIBILITY_PRIVATE #define VISIBILITY_PRIVATE private: #endif #include 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". */ string get_comm(); /*! \brief Return the name of the process containing this thread from argv[0], e.g. "/bin/top". */ string get_exe(); /*! \brief Return the full executable path of the process containing this thread, e.g. "/bin/top". */ string get_exepath(); /*! \brief Return the working directory of the process containing this thread. */ string get_cwd(); /*! \brief Return the values of all environment variables for the process containing this thread. */ const 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. */ string get_env(const 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() { if(m_main_thread == NULL) { // // 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. // sinsp_threadinfo* ptinfo = lookup_thread(); if(NULL == ptinfo) { return NULL; } m_main_thread = ptinfo; } } return m_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 Retrive 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) { return fdt->find(fd); } 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 heirarchy, 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); // // 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. string m_comm; ///< Command name (e.g. "top") string m_exe; ///< argv[0] (e.g. "sshd: user@pts/4") string m_exepath; ///< full executable path vector m_args; ///< Command line arguments (e.g. "-d1") vector m_env; ///< Environment variables vector> m_cgroups; ///< subsystem-cgroup pairs 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. string m_root; size_t m_program_hash; size_t m_program_hash_falco; int32_t m_tty; // // 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; #ifdef HAS_FILTERING // // State for filtering // uint64_t m_last_latency_entertime; uint64_t m_latency; #endif // // Global state // sinsp *m_inspector; 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); 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(); sinsp_threadinfo* lookup_thread(); inline void args_to_scap(scap_threadinfo* sctinfo); inline void env_to_scap(scap_threadinfo* sctinfo); inline void cgroups_to_scap(scap_threadinfo* sctinfo); 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 string m_cwd; // current working directory sinsp_threadinfo* m_main_thread; uint8_t* m_lastevent_data; // Used by some event parsers to store the last enter event 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; }; /*@}*/ typedef unordered_map threadinfo_map_t; typedef threadinfo_map_t::iterator threadinfo_map_iterator_t; /////////////////////////////////////////////////////////////////////////////// // 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: 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); void 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; } set m_server_ports; private: void remove_thread(threadinfo_map_iterator_t it, bool force); void increment_mainthread_childcount(sinsp_threadinfo* threadinfo); inline void clear_thread_pointers(threadinfo_map_iterator_t it); void free_dump_fdinfos(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; sinsp_threadinfo* m_last_tinfo; uint64_t m_last_flush_time_ns; uint32_t m_n_drops; uint32_t m_n_proc_lookups; 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.19.1/userspace/libsinsp/tracers.cpp000066400000000000000000000561151316537151600211100ustar00rootroot00000000000000/* 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 "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 braket // 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.19.1/userspace/libsinsp/tracers.h000066400000000000000000000113331316537151600205460ustar00rootroot00000000000000/* 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 . */ #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.19.1/userspace/libsinsp/tuples.h000066400000000000000000000045201316537151600204170ustar00rootroot00000000000000/* 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 . */ #pragma once /** @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; /*! \brief An IPv6 tuple. */ typedef union _ipv6tuple { struct { uint32_t m_sip[4]; ///< source (i.e. client) address. uint32_t m_dip[4]; ///< 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 { uint32_t m_ip[4]; ///< 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.19.1/userspace/libsinsp/uri.cpp000066400000000000000000000136541316537151600202450ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/uri.h000066400000000000000000000062041316537151600177030ustar00rootroot00000000000000// // 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.19.1/userspace/libsinsp/uri_parser.c000066400000000000000000000410551316537151600212550ustar00rootroot00000000000000/* 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 delimeters */ 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.19.1/userspace/libsinsp/uri_parser.h000066400000000000000000000072441316537151600212640ustar00rootroot00000000000000/* 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.19.1/userspace/libsinsp/user_event.cpp000066400000000000000000000250741316537151600216240ustar00rootroot00000000000000/* 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 "sinsp.h" #include "sinsp_int.h" #include "user_event.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(uint64_t timestamp, std::string&& name, std::string&& description, event_scope&& scope, tag_map_t&& tags, uint32_t sev) { const std::string from("\""); const std::string to("\\\""); std::ostringstream ostr; ostr << "timestamp: " << timestamp << '\n' << "name: \"" << replace_in_place(name, from, to) << "\"\n" "description: \"" << replace_in_place(description, from, to) << "\"\n" "scope: \"" << replace_in_place(scope.get_ref(), from, to) << "\"\n"; if(sev != UNKNOWN_SEVERITY) { ostr << "priority: " << sev << '\n'; } if(tags.size()) { ostr << "tags:"; for(auto& tag : tags) { ostr << "\n \"" << replace(tag.first, from, to) << "\": \"" << replace_in_place(tag.second, from, to) << '"'; } } ostr << std::flush; g_logger.log(ostr.str(), sinsp_logger::SEV_DEBUG); 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}}; g_logger.log(sinsp_user_event::to_string(get_epoch_utc_seconds_now(), std::move(event_name), description.str(), std::move(scope), std::move(tags)), sinsp_logger::SEV_EVT_WARNING); } sysdig-0.19.1/userspace/libsinsp/user_event.h000066400000000000000000000220251316537151600212620ustar00rootroot00000000000000/* 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 . */ #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(), a.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; static std::string to_string(uint64_t timestamp, std::string&& name, std::string&& description, event_scope&& scope, tag_map_t&& tags, uint32_t sev = UNKNOWN_SEVERITY); 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 300u; // 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.19.1/userspace/libsinsp/utils.cpp000066400000000000000000000716051316537151600206060ustar00rootroot00000000000000/* 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 . */ #ifndef _WIN32 #include #include #include #include #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 char* realpath_ex(const char *path, char *buff) { char *home; if(*path=='~' && (home = getenv("HOME"))) { char s[PATH_MAX]; return realpath(strcat(strcpy(s, home), path+1), buff); } else { return realpath(path, buff); } } #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 char resolved_path[PATH_MAX]; if(realpath_ex(g_chisel_dirs_array[j].m_dir, resolved_path) != NULL) { string resolved_path_str(resolved_path); if(resolved_path_str[resolved_path_str.size() -1] != '/') { resolved_path_str += "/"; } chiseldir_info cdi; cdi.m_need_to_resolve = false; sprintf(cdi.m_dir, "%s", resolved_path_str.c_str()); 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"; 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; uint8_t* dip6 = (uint8_t*)sinfo->m_ipv6info.m_fields.m_dip; uint8_t* sip = ((uint8_t*)(sinfo->m_ipv6info.m_fields.m_sip)) + 12; uint8_t* dip = ((uint8_t*)(sinfo->m_ipv6info.m_fields.m_dip)) + 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_ipv4info.m_fields.m_sport, sinfo->m_ipv6info.m_fields.m_l4proto, resolve).c_str(), dststr, port_to_string(sinfo->m_ipv4info.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)) { ASSERT(false); 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 _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 /////////////////////////////////////////////////////////////////////////////// // Time utility functions. /////////////////////////////////////////////////////////////////////////////// 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]; // IP address is saved with host byte order, that's why we do shifts snprintf(buf, sizeof(buf), "%d.%d.%d.%d:%s", (addr->m_ip & 0xFF), ((addr->m_ip & 0xFF00) >> 8), ((addr->m_ip & 0xFF0000) >> 16), ((addr->m_ip & 0xFF000000) >> 24), 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, 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, source_address, 100)) { return string(); } if(NULL == inet_ntop(AF_INET6, tuple->m_fields.m_dip, 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_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; } /////////////////////////////////////////////////////////////////////////////// // 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.19.1/userspace/libsinsp/utils.h000066400000000000000000000242761316537151600202550ustar00rootroot00000000000000/* 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 . */ #pragma once #include #include #include #include #include #include #include #include #include "json/json.h" class sinsp_evttables; typedef union _sinsp_sockinfo sinsp_sockinfo; typedef union _ipv4tuple ipv4tuple; typedef union _ipv6tuple ipv6tuple; typedef struct ipv4serverinfo ipv4serverinfo; typedef struct ipv6serverinfo ipv6serverinfo; class filter_check_info; /////////////////////////////////////////////////////////////////////////////// // 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); // // 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 }; /////////////////////////////////////////////////////////////////////////////// // 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 slighly 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); sysdig-0.19.1/userspace/libsinsp/value_parser.cpp000066400000000000000000000064351316537151600221350ustar00rootroot00000000000000#include "sinsp.h" #include "sinsp_int.h" #include "value_parser.h" #ifdef _WIN32 #pragma comment(lib, "Ws2_32.lib") #include #else #include #endif void 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) { switch(ptype) { case PT_INT8: *(int8_t*)storage = sinsp_numparser::parsed8(str); break; case PT_INT16: *(int16_t*)storage = sinsp_numparser::parsed16(str); break; case PT_INT32: *(int32_t*)storage = sinsp_numparser::parsed32(str); break; case PT_INT64: case PT_FD: case PT_ERRNO: *(int64_t*)storage = sinsp_numparser::parsed64(str); break; case PT_L4PROTO: // This can be resolved in the future case PT_FLAGS8: case PT_UINT8: *(uint8_t*)storage = sinsp_numparser::parseu8(str); 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); } } } break; } case PT_FLAGS16: case PT_UINT16: *(uint16_t*)storage = sinsp_numparser::parseu16(str); break; case PT_FLAGS32: case PT_UINT32: *(uint32_t*)storage = sinsp_numparser::parseu32(str); break; case PT_UINT64: *(uint64_t*)storage = sinsp_numparser::parseu64(str); break; case PT_RELTIME: case PT_ABSTIME: *(uint64_t*)storage = sinsp_numparser::parseu64(str); 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; } break; case PT_BOOL: 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_IPV4ADDR: if(inet_pton(AF_INET, str, storage) != 1) { throw sinsp_exception("unrecognized IP address " + string(str)); } 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); break; } default: ASSERT(false); throw sinsp_exception("wrong parameter type " + to_string((long long) ptype)); } } sysdig-0.19.1/userspace/libsinsp/value_parser.h000066400000000000000000000016771316537151600216050ustar00rootroot00000000000000/* 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 . */ #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 void string_to_rawval(const char* str, uint32_t len, uint8_t *storage, string::size_type max_len, ppm_param_type ptype); }; sysdig-0.19.1/userspace/libsinsp/viewinfo.cpp000066400000000000000000000201531316537151600212640ustar00rootroot00000000000000/* 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 "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.19.1/userspace/libsinsp/viewinfo.h000066400000000000000000000116261316537151600207360ustar00rootroot00000000000000/* Copyright (C) 2013-2015 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 #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.19.1/userspace/sysdig.project000066400000000000000000010220441316537151600200030ustar00rootroot00000000000000 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.19.1/userspace/sysdig/000077500000000000000000000000001316537151600164105ustar00rootroot00000000000000sysdig-0.19.1/userspace/sysdig/CMakeLists.txt000066400000000000000000000045731316537151600211610ustar00rootroot00000000000000include_directories("${JSONCPP_INCLUDE}") if(NOT WIN32) if(NOT APPLE) include_directories("${CURL_INCLUDE_DIR}") endif() 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) 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.19.1/userspace/sysdig/chisels/000077500000000000000000000000001316537151600200425ustar00rootroot00000000000000sysdig-0.19.1/userspace/sysdig/chisels/COPYING000066400000000000000000000432541316537151600211050ustar00rootroot00000000000000 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.19.1/userspace/sysdig/chisels/ansiterminal.lua000066400000000000000000000042661316537151600232430ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 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.setbgcol(color) io.write(schar(27) .. '[' .. "48;5;" .. color .. "m") end return ansiterminal sysdig-0.19.1/userspace/sysdig/chisels/around.lua000066400000000000000000000070411316537151600220370ustar00rootroot00000000000000--[[ Copyright (C) 2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/bottlenecks.lua000066400000000000000000000047701316537151600230720ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/common.lua000066400000000000000000000171731316537151600220460ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] --[[ 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 --[[ 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.19.1/userspace/sysdig/chisels/dkjson.lua000066400000000000000000000637401316537151600220470ustar00rootroot00000000000000 -- 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.19.1/userspace/sysdig/chisels/echo_fds.lua000066400000000000000000000100231316537151600223130ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/fdbytes_by.lua000066400000000000000000000027441316537151600227060ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/fdcount_by.lua000066400000000000000000000044631316537151600227100ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/fdtime_by.lua000066400000000000000000000026351316537151600225150ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/fileslower.lua000066400000000000000000000116241316537151600227240ustar00rootroot00000000000000--[[ 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". Copyright (C) 2014 Brendan Gregg. 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 . --]] -- 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.19.1/userspace/sysdig/chisels/flame.lua000066400000000000000000000300101316537151600216230ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/http.lua000066400000000000000000000060511316537151600215260ustar00rootroot00000000000000--[[ Copyright (C) 2015 Luca Marturana 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 . --]] -- 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.19.1/userspace/sysdig/chisels/httplog.lua000066400000000000000000000032421316537151600222270ustar00rootroot00000000000000--[[ Copyright (C) 2015 Luca Marturana 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 . --]] -- 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.19.1/userspace/sysdig/chisels/httptop.lua000066400000000000000000000112071316537151600222500ustar00rootroot00000000000000--[[ Copyright (C) 2015 Luca Marturana 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 . --]] -- 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.19.1/userspace/sysdig/chisels/iobytes.lua000066400000000000000000000032021316537151600222200ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/iobytes_file.lua000066400000000000000000000031021316537151600232160ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/iobytes_net.lua000066400000000000000000000032031316537151600230670ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/list_login_shells.lua000066400000000000000000000064361316537151600242730ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/lscontainers.lua000066400000000000000000000047561316537151600232650ustar00rootroot00000000000000--[[ Copyright (C) 2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/lsof.lua000066400000000000000000000054001316537151600215070ustar00rootroot00000000000000--[[ Copyright (C) 2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/memcachelog.lua000066400000000000000000000053411316537151600230140ustar00rootroot00000000000000--[[ 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. Copyright (C) 2015 Donatas Abraitis. 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 . --]] -- 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.19.1/userspace/sysdig/chisels/netlower.lua000066400000000000000000000110701316537151600224030ustar00rootroot00000000000000--[[ 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 Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/netstat.lua000066400000000000000000000054211316537151600222310ustar00rootroot00000000000000--[[ Copyright (C) 2014 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 . --]] -- 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)" 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.19.1/userspace/sysdig/chisels/proc_exec_time.lua000066400000000000000000000104661316537151600235410ustar00rootroot00000000000000--[[ 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 Copyright (C) 2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/ps.lua000066400000000000000000000053251316537151600211740ustar00rootroot00000000000000--[[ Copyright (C) 2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/scallslower.lua000066400000000000000000000102571316537151600231040ustar00rootroot00000000000000--[[ 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 Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/shellshock_detect.lua000066400000000000000000000041751316537151600242430ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/spectrogram.lua000066400000000000000000000100451316537151600230730ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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} -- 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 function mkcol(n) local col = math.floor(math.log10(n * refresh_per_sec + 1) / math.log10(1.6)) if col < 1 then col = 1 end if col > #colpalette then col = #colpalette end return colpalette[col] end -- Periodic timeout callback function on_interval(ts_s, ts_ns, delta) terminal.moveup(1) for x = 1, w do local fr = frequencies[x] if fr == nil or fr == 0 then terminal.setbgcol(0) else terminal.setbgcol(mkcol(fr)) end io.write(" ") 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.19.1/userspace/sysdig/chisels/spy_file.lua000066400000000000000000000072721316537151600223670ustar00rootroot00000000000000--[[ Copyright (C) 2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/spy_ip.lua000066400000000000000000000051701316537151600220530ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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 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.19.1/userspace/sysdig/chisels/spy_logs.lua000066400000000000000000000150371316537151600224120ustar00rootroot00000000000000--[[ Copyright (C) 2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/spy_port.lua000066400000000000000000000052351316537151600224310ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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 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------ 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.19.1/userspace/sysdig/chisels/spy_syslog.lua000066400000000000000000000132411316537151600227610ustar00rootroot00000000000000--[[ Copyright (C) 2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/spy_users.lua000066400000000000000000000143751316537151600226130ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/statsd.lua000066400000000000000000000102121316537151600220430ustar00rootroot00000000000000-- 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.19.1/userspace/sysdig/chisels/stderr.lua000066400000000000000000000053041316537151600220520ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/stdin.lua000066400000000000000000000024141316537151600216670ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/stdout.lua000066400000000000000000000024171316537151600220730ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/subsecoffset.lua000066400000000000000000000105561316537151600232470ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 Draios inc. Copyright (C) 2015 Brendan Gregg. 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 . --]] -- 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} -- 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 function mkcol(n) local col = math.floor(math.log10(n * refresh_per_sec + 1) / math.log10(1.6)) if col < 1 then col = 1 end if col > #colpalette then col = #colpalette end return colpalette[col] end -- Periodic timeout callback function on_interval(ts_s, ts_ns, delta) terminal.moveup(1) for x = 1, w do local fr = frequencies[x] if fr == nil or fr == 0 then terminal.setbgcol(0) else terminal.setbgcol(mkcol(fr)) end io.write(" ") 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.19.1/userspace/sysdig/chisels/table_generator.lua000066400000000000000000000104441316537151600237050ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topconns.lua000066400000000000000000000033461316537151600224160ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topcontainers_cpu.lua000066400000000000000000000053541316537151600243130ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topcontainers_error.lua000066400000000000000000000021351316537151600246470ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topcontainers_file.lua000066400000000000000000000022621316537151600244360ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topcontainers_net.lua000066400000000000000000000020611316537151600243020ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topfiles_bytes.lua000066400000000000000000000031701316537151600236010ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topfiles_errors.lua000066400000000000000000000031451316537151600237710ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topfiles_time.lua000066400000000000000000000031511316537151600234100ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topports_server.lua000066400000000000000000000033151316537151600240270ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topprocs_cpu.lua000066400000000000000000000064471316537151600233000ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topprocs_errors.lua000066400000000000000000000031371316537151600240160ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topprocs_file.lua000066400000000000000000000033221316537151600234150ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topprocs_net.lua000066400000000000000000000031401316537151600232620ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topscalls.lua000066400000000000000000000033241316537151600225530ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/topscalls_time.lua000066400000000000000000000032461316537151600235740ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/tracers_2_statsd.lua000066400000000000000000000037021316537151600240150ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] -- 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.19.1/userspace/sysdig/chisels/v_backlog.lua000066400000000000000000000040501316537151600224730ustar00rootroot00000000000000--[[ Copyright (C) Donatas Abraitis. 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 . --]] 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.19.1/userspace/sysdig/chisels/v_connections.lua000066400000000000000000000077751316537151600234340ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_containers.lua000066400000000000000000000111301316537151600232330ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_containers_errors.lua000066400000000000000000000113641316537151600246400ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_cpus.lua000066400000000000000000000033131316537151600220440ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_directories.lua000066400000000000000000000063241316537151600234130ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_docker_events.lua000066400000000000000000000032031316537151600237230ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_errors.lua000066400000000000000000000040601316537151600224060ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_file_opens.lua000066400000000000000000000047231316537151600232230ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_files.lua000066400000000000000000000066571316537151600222120ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_incoming_connections.lua000066400000000000000000000035621316537151600253050ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_io_by_type.lua000066400000000000000000000051661316537151600232440ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_kubernetes_controllers.lua000066400000000000000000000060261316537151600256730ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_kubernetes_deployments.lua000066400000000000000000000061361316537151600256720ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_kubernetes_namespaces.lua000066400000000000000000000056621316537151600254510ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_kubernetes_pods.lua000066400000000000000000000067141316537151600242760ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_kubernetes_replicasets.lua000066400000000000000000000060351316537151600256430ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_kubernetes_services.lua000066400000000000000000000060131316537151600251440ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_marathon_apps.lua000066400000000000000000000047321316537151600237340ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_marathon_groups.lua000066400000000000000000000046321316537151600243070ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_mesos_frameworks.lua000066400000000000000000000050321316537151600244600ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_mesos_tasks.lua000066400000000000000000000050741316537151600234330ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_notifications.lua000066400000000000000000000027501316537151600237470ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_page_faults.lua000066400000000000000000000053701316537151600233710ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_port_bindings.lua000066400000000000000000000031471316537151600237400ustar00rootroot00000000000000--[[ Copyright (C) 2017 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 . --]] 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 (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.19.1/userspace/sysdig/chisels/v_procs.lua000066400000000000000000000107241316537151600222240ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_procs_cpu.lua000066400000000000000000000073221316537151600230730ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_procs_errors.lua000066400000000000000000000077061316537151600236260ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_procs_fd_usage.lua000066400000000000000000000072311316537151600240600ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_slow_io.lua000066400000000000000000000043261316537151600225520ustar00rootroot00000000000000--[[ Copyright (C) 2017 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 . --]] 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.19.1/userspace/sysdig/chisels/v_spans_list.lua000066400000000000000000000045611316537151600232570ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_spans_summary.lua000066400000000000000000000057101316537151600237760ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_spectro_all.lua000066400000000000000000000021601316537151600234000ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_spectro_file.lua000066400000000000000000000026741316537151600235610ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_spectro_traces.lua000066400000000000000000000043311316537151600241130ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_sports.lua000066400000000000000000000044021316537151600224240ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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 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.19.1/userspace/sysdig/chisels/v_spy_syslog.lua000066400000000000000000000041051316537151600233050ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_spy_users.lua000066400000000000000000000043771316537151600231410ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_spy_users_wsysdig.lua000066400000000000000000000046661316537151600247130ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_syscall_procs.lua000066400000000000000000000035371316537151600237620ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_syscalls.lua000066400000000000000000000046111316537151600227310ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_threads.lua000066400000000000000000000061621316537151600225310ustar00rootroot00000000000000--[[ Copyright (C) 2013-2014 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 . --]] 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.19.1/userspace/sysdig/chisels/v_traces_list.lua000066400000000000000000000050661316537151600234150ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/v_traces_summary.lua000066400000000000000000000062741316537151600241410ustar00rootroot00000000000000--[[ Copyright (C) 2013-2015 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 . --]] 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.19.1/userspace/sysdig/chisels/wsysdig_summary.lua000066400000000000000000001250461316537151600240230ustar00rootroot00000000000000--[[ Copyright (C) 2017 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 . --]] -- 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 container_evt_types = { {'Container', 'Attached' }, {'Container', 'Committed' }, {'Container', 'Copied' }, {'Container', 'Created' }, {'Container', 'Destroyed' }, {'Container', 'Died' }, {'Container', 'Exec Created' }, {'Container', 'Exec Started' }, {'Container', 'Exported' }, {'Container', 'Killed' }, {'Container', 'Out of Memory' }, {'Container', 'Paused' }, {'Container', 'Renamed' }, {'Container', 'Resized' }, {'Container', 'Restarted' }, {'Container', 'Started' }, {'Container', 'Stopped' }, {'Container', 'Top' }, {'Container', 'Unpaused' }, {'Container', 'Updated' }, {'Image', 'Deleted' }, {'Image', 'Imported' }, {'Image', 'Pulled' }, {'Image', 'Pushed' }, {'Image', 'Tagged' }, {'Image', 'Untagged' }, {'Volume', 'Mounted' }, {'Volume', 'Unmounted' }, {'Network', 'Connected' }, {'Network', 'Disconnected' } } 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) for i, v in ipairs(container_evt_types) do local ccat = 'dockerEvtsCount' .. v[1] .. " " .. v[2] s[ccat] = create_category_basic(true, true) 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 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 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 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 accessed 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 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 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 for i, v in ipairs(container_evt_types) do local ccat = 'dockerEvtsCount' .. v[1] .. ' ' .. v[2] if should_include(gsummary[ccat]) then res[#res+1] = { name = v[1] .. ' ' .. v[2] .. ' Events', desc = 'Total number of docker events of type ' .. v[1] .. ' ' .. v[2], category = 'infrastructure', targetView = 'docker_events', targetViewFilter = 'evt.arg.name="' .. v[1] .. ' ' .. v[2] .. '"' , drillDownKey = 'NONE', data = gsummary[ccat] } has_cat_infrastructure = true end 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.19.1/userspace/sysdig/config_sysdig.h.in000066400000000000000000000001641316537151600220160ustar00rootroot00000000000000#pragma once #define SYSDIG_VERSION "${SYSDIG_VERSION}" #define SYSDIG_INSTALLATION_DIR "${CMAKE_INSTALL_PREFIX}" sysdig-0.19.1/userspace/sysdig/csysdig.cpp000066400000000000000000000551041316537151600205660ustar00rootroot00000000000000/* 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 . */ #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" " -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" " --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; continue; } } 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; #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' }, {"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 }, {"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, "Ad: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 '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 == "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; } } 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(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 disply 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(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(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.19.1/userspace/sysdig/fields_info.cpp000066400000000000000000000142571316537151600214060ustar00rootroot00000000000000/* 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 . */ // // Variuos 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) { uint32_t j, l, m; int32_t k; if(markdown) { 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(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(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 meaure 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.19.1/userspace/sysdig/man/000077500000000000000000000000001316537151600171635ustar00rootroot00000000000000sysdig-0.19.1/userspace/sysdig/man/CMakeLists.txt000066400000000000000000000013251316537151600217240ustar00rootroot00000000000000find_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.19.1/userspace/sysdig/man/build.sh000077500000000000000000000001661316537151600206240ustar00rootroot00000000000000pandoc -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.19.1/userspace/sysdig/man/csysdig.8000066400000000000000000000307201316537151600207230ustar00rootroot00000000000000.\" 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.19.1/userspace/sysdig/man/csysdig.md000066400000000000000000000260731316537151600211620ustar00rootroot00000000000000NAME ---- 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.19.1/userspace/sysdig/man/sysdig.8000066400000000000000000000402121316537151600205550ustar00rootroot00000000000000.\" 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.19.1/userspace/sysdig/man/sysdig.md000066400000000000000000000342141316537151600210130ustar00rootroot00000000000000NAME ---- 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 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.19.1/userspace/sysdig/sysdig.cpp000066400000000000000000001263611316537151600204270ustar00rootroot00000000000000/* 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 . */ #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" #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. Savefiles 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" " -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 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" " -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. Savefiles 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" " --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" " -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 tracefiles.\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++) { 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 != NULL) { 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; if(!json) { cout << 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; int cflag = 0; bool jflag = false; bool unbuf_flag = false; bool filter_proclist_flag = false; string cname; vector* summary_table = NULL; string* k8s_api = 0; string* k8s_api_cert = 0; string* mesos_api = 0; bool force_tracers_capture = false; bool page_faults = false; // 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' }, #ifdef HAS_CHISELS {"chisel", required_argument, 0, 'c' }, {"list-chisels", no_argument, &cflag, 1 }, #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' }, {"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' }, {"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, "Abc:" "C:" "dDEe:F" "G:" "hi:jk:K:lLm:M:n:Pp:qRr:Ss:t:Tv" "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 0: if(cflag != 1 && cflag != 2) { break; } if(cflag == 2) { cname = optarg; } #ifdef HAS_CHISELS case 'c': { if(cflag == 0) { string ostr(optarg); if(ostr.size() >= 1) { if(ostr == "l") { cflag = 1; } } } if(cflag == 1) { 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, optarg); 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': summary_table = new vector; 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 '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; // getopt_long : '?' for an ambiguous match or an extraneous parameter case '?': delete inspector; return sysdig_init_res(EXIT_FAILURE); break; default: break; } if(string(long_options[long_index].name) == "version") { printf("sysdig version %s\n", SYSDIG_VERSION); delete inspector; return sysdig_init_res(EXIT_SUCCESS); } if(string(long_options[long_index].name) == "unbuffered") { unbuf_flag = true; } if(string(long_options[long_index].name) == "filter-proclist") { filter_proclist_flag = true; } if(string(long_options[long_index].name) == "list-markdown") { list_flds = true; list_flds_markdown = true; } if(string(long_options[long_index].name) == "page-faults") { page_faults = true; } } // // 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 // // 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(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 "\n", cstats.n_evts, cstats.n_drops); 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 = EXIT_FAILURE; } 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 there's a summary table, sort and print it // if(summary_table != NULL) { 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.19.1/userspace/sysdig/sysdig.h000066400000000000000000000036111316537151600200640ustar00rootroot00000000000000/* 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 . */ #pragma once #include #ifdef HAS_CAPTURE #include "../../driver/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_id = id; m_ncalls = 0; m_is_unsupported_syscall = is_unsupported_syscall; } uint16_t m_id; uint64_t m_ncalls; 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); 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.19.1/userspace/sysdig/sysdig.vcxproj000066400000000000000000000120411316537151600213250ustar00rootroot00000000000000 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.19.1/userspace/sysdig/win32/000077500000000000000000000000001316537151600173525ustar00rootroot00000000000000sysdig-0.19.1/userspace/sysdig/win32/getopt.c000066400000000000000000000634131316537151600210270ustar00rootroot00000000000000/* 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.19.1/userspace/sysdig/win32/getopt.h000066400000000000000000000114461316537151600210330ustar00rootroot00000000000000/* 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.19.1/userspace/userspace.sln000066400000000000000000000041461316537151600176230ustar00rootroot00000000000000 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