pax_global_header00006660000000000000000000000064126547205750014527gustar00rootroot0000000000000052 comment=c34bde9b633abd5b50ae95dffddeb343ed267876 sysdig-0.8.0/000077500000000000000000000000001265472057500130365ustar00rootroot00000000000000sysdig-0.8.0/.gitignore000066400000000000000000000001531265472057500150250ustar00rootroot00000000000000*.o *.ko *.ko.unsigned *.cmd *.symvers *.order *.mod *.mod.c build/ driver/Makefile driver/driver_config.h sysdig-0.8.0/.travis.yml000066400000000000000000000043511265472057500151520ustar00rootroot00000000000000language: c env: - BUILD_TYPE=Debug - BUILD_TYPE=Release before_install: - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - sudo apt-get update install: - sudo apt-get install g++-4.8 - sudo apt-get install rpm linux-headers-$(uname -r) - sudo apt-get purge libncurses5-dev cmake libcurl4-openssl-dev zlib1g-dev before_script: - export KERNELDIR=/lib/modules/$(ls /lib/modules | sort | head -1)/build script: - 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/ - 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_BRANCH notifications: webhooks: urls: - https://webhooks.gitter.im/e/fdbc2356fb0ea2f15033 on_success: change on_failure: always on_start: neversysdig-0.8.0/CMakeCPackOptions.cmake000066400000000000000000000001451265472057500172760ustar00rootroot00000000000000if(CPACK_GENERATOR MATCHES "TGZ") set(CPACK_SET_DESTDIR "ON") set(CPACK_STRIP_FILES "OFF") endif() sysdig-0.8.0/CMakeLists.txt000066400000000000000000000273161265472057500156070ustar00rootroot00000000000000# 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 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 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 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 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 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 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_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.2d.tar.gz" URL_MD5 "38dd619b2e77cbac69b99f52a053d25a" CONFIGURE_COMMAND ./config shared --prefix=${OPENSSL_INSTALL_DIR} BUILD_COMMAND ${CMD_MAKE} BUILD_IN_SOURCE 1 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 "") else() set(CURL_SSL_OPTION "--with-ssl=${OPENSSL_INSTALL_DIR}") endif() message(STATUS "Using bundled curl in '${CURL_BUNDLE_DIR}'") message(STATUS "Using SSL for curl in '${CURL_SSL_OPTION}'") ExternalProject_Add(curl DEPENDS openssl URL "http://download.draios.com/dependencies/curl-7.45.0.tar.bz2" URL_MD5 "62c1a352b28558f25ba6209214beadc8" 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-libmetalink --without-librtmp --without-winidn --without-libidn --without-nghttp2 BUILD_COMMAND ${CMD_MAKE} BUILD_IN_SOURCE 1 INSTALL_COMMAND "") endif() endif() # NOT WIN32 AND NOT APPLE add_subdirectory(userspace/sysdig) add_subdirectory(userspace/libscap) add_subdirectory(userspace/libsinsp) 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.8.0/COPYING000066400000000000000000000432541265472057500141010ustar00rootroot00000000000000 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.8.0/README.md000066400000000000000000000064271265472057500143260ustar00rootroot00000000000000![](https://ga-beacon.appspot.com/UA-40398182-5/sysdig/README?pixel) sysdig ====== [![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) for the Chisel of the Week * This is our [blog] (https://sysdig.com/blog/). There are many like it, but this one is ours. * Join our IRC channel `#sysdig` on [Freenode](http://webchat.freenode.net/?channels=sysdig) Sysdig Cloud --- Interested in a fully supported, fully distributed version of sysdig? Check out [Sysdig Cloud] (https://sysdig.com/)! Sysdig is proudly supported by [Sysdig Inc] (https://sysdig.com/). Interested in what we're doing? [Sysdig is hiring] (https://sysdig.com/jobs/). sysdig-0.8.0/coding_conventions.md000066400000000000000000000142471265472057500172600ustar00rootroot000000000000000 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/Documentation/CodingStyle 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.8.0/common/000077500000000000000000000000001265472057500143265ustar00rootroot00000000000000sysdig-0.8.0/common/inttypes_win.h000066400000000000000000000173021265472057500172360ustar00rootroot00000000000000// 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.8.0/conf/000077500000000000000000000000001265472057500137635ustar00rootroot00000000000000sysdig-0.8.0/conf/dev.conf000066400000000000000000000000511265472057500154040ustar00rootroot00000000000000export SYSDIG_VERSION=0.99.$BUILD_NUMBER sysdig-0.8.0/docker/000077500000000000000000000000001265472057500143055ustar00rootroot00000000000000sysdig-0.8.0/docker/dev/000077500000000000000000000000001265472057500150635ustar00rootroot00000000000000sysdig-0.8.0/docker/dev/Dockerfile000066400000000000000000000031021265472057500170510ustar00rootroot00000000000000FROM debian:unstable MAINTAINER Sysdig ENV SYSDIG_REPOSITORY dev LABEL RUN="docker run -i -t -v /var/run/docker.sock:/host/var/run/docker.sock -v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro --name NAME IMAGE" ENV SYSDIG_HOST_ROOT /host ENV HOME /root RUN cp /etc/skel/.bashrc /root && cp /etc/skel/.profile /root ADD http://download.draios.com/apt-draios-priority /etc/apt/preferences.d/ RUN apt-get update \ && apt-get install -y --no-install-recommends \ bash-completion \ curl \ ca-certificates \ gcc \ gcc-4.9 \ gcc-4.8 # Terribly terrible hacks: 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.8 RUN rm -rf /usr/bin/gcc \ && ln -s /usr/bin/gcc-4.9 /usr/bin/gcc \ && ln -s /usr/bin/gcc-4.8 /usr/bin/gcc-4.7 \ && ln -s /usr/bin/gcc-4.8 /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.8.0/docker/dev/docker-entrypoint.sh000077500000000000000000000003161265472057500211020ustar00rootroot00000000000000#!/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.8.0/docker/stable/000077500000000000000000000000001265472057500155575ustar00rootroot00000000000000sysdig-0.8.0/docker/stable/Dockerfile000066400000000000000000000031051265472057500175500ustar00rootroot00000000000000FROM debian:unstable MAINTAINER Sysdig ENV SYSDIG_REPOSITORY stable LABEL RUN="docker run -i -t -v /var/run/docker.sock:/host/var/run/docker.sock -v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro --name NAME IMAGE" ENV SYSDIG_HOST_ROOT /host ENV HOME /root RUN cp /etc/skel/.bashrc /root && cp /etc/skel/.profile /root ADD http://download.draios.com/apt-draios-priority /etc/apt/preferences.d/ RUN apt-get update \ && apt-get install -y --no-install-recommends \ bash-completion \ curl \ ca-certificates \ gcc \ gcc-4.9 \ gcc-4.8 # Terribly terrible hacks: 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.8 RUN rm -rf /usr/bin/gcc \ && ln -s /usr/bin/gcc-4.9 /usr/bin/gcc \ && ln -s /usr/bin/gcc-4.8 /usr/bin/gcc-4.7 \ && ln -s /usr/bin/gcc-4.8 /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.8.0/docker/stable/docker-entrypoint.sh000077500000000000000000000003161265472057500215760ustar00rootroot00000000000000#!/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.8.0/driver/000077500000000000000000000000001265472057500143315ustar00rootroot00000000000000sysdig-0.8.0/driver/CMakeLists.txt000066400000000000000000000046351265472057500171010ustar00rootroot00000000000000option(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) 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.8.0/driver/Makefile.in000066400000000000000000000006421265472057500164000ustar00rootroot00000000000000@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.8.0/driver/dkms.conf.in000066400000000000000000000002361265472057500165440ustar00rootroot00000000000000PACKAGE_NAME="@PACKAGE_NAME@" PACKAGE_VERSION="@PROBE_VERSION@" BUILT_MODULE_NAME[0]="@PROBE_NAME@" DEST_MODULE_LOCATION[0]="/kernel/extra" AUTOINSTALL="yes" sysdig-0.8.0/driver/driver_config.h.in000066400000000000000000000003121265472057500177230ustar00rootroot00000000000000#pragma once #define PROBE_VERSION "${PROBE_VERSION}" #define PROBE_NAME "${PROBE_NAME}" #define PROBE_DEVICE_NAME "${PROBE_DEVICE_NAME}" #define PROBE_EVENT_DEVICE_NAME PROBE_DEVICE_NAME "-events" sysdig-0.8.0/driver/dynamic_params_table.c000066400000000000000000000014661265472057500206420ustar00rootroot00000000000000/* 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.8.0/driver/event_table.c000066400000000000000000001233771265472057500170020ustar00rootroot00000000000000/* 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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 0}, /* PPME_SYSCALL_OPEN_X */{"open", EC_FILE, (enum ppm_event_flags)(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_HEX} } }, /* PPME_SYSCALL_CLOSE_E */{"close", EC_IO_OTHER, (enum ppm_event_flags)(EF_DESTROYS_FD | EF_USES_FD | EF_MODIFIES_STATE), 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_CLOSE_X */{"close", EC_IO_OTHER, (enum ppm_event_flags)(EF_DESTROYS_FD | EF_USES_FD | EF_MODIFIES_STATE), 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_READ_E */{"read", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_READ_X */{"read", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_WRITE_E */{"write", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD), 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_WRITE_X */{"write", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD), 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, (enum ppm_event_flags)(EF_MODIFIES_STATE | EF_OLD_VERSION), 0}, /* PPME_SYSCALL_EXECVE_8_X */{"execve", EC_PROCESS, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_MODIFIES_STATE | EF_OLD_VERSION), 0}, /* PPME_CLONE_11_X */{"clone", EC_PROCESS, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SOCKET_BIND_E */{"bind", EC_NET, EF_USES_FD, 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SOCKET_BIND_X */{"bind", EC_NET, EF_USES_FD, 2, {{"res", PT_ERRNO, PF_DEC}, {"addr", PT_SOCKADDR, PF_NA} } }, /* PPME_SOCKET_CONNECT_E */{"connect", EC_NET, (enum ppm_event_flags)(EF_USES_FD | EF_MODIFIES_STATE), 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SOCKET_CONNECT_X */{"connect", EC_NET, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE | EF_OLD_VERSION), 0}, /* PPME_SOCKET_ACCEPT_X */{"accept", EC_NET, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD), 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_SEND_X */{"send", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD), 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SOCKET_SENDTO_E */{"sendto", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD | EF_MODIFIES_STATE), 3, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"tuple", PT_SOCKTUPLE, PF_NA} } }, /* PPME_SOCKET_SENDTO_X */{"sendto", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD | EF_MODIFIES_STATE), 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SOCKET_RECV_E */{"recv", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SOCKET_RECV_X */{"recv", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SOCKET_RECVFROM_E */{"recvfrom", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE), 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SOCKET_RECVFROM_X */{"recvfrom", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE), 3, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA}, {"tuple", PT_SOCKTUPLE, PF_NA} } }, /* PPME_SOCKET_SHUTDOWN_E */{"shutdown", EC_NET, (enum ppm_event_flags)(EF_USES_FD | EF_MODIFIES_STATE), 2, {{"fd", PT_FD, PF_DEC}, {"how", PT_FLAGS8, PF_HEX, shutdown_how} } }, /* PPME_SOCKET_SHUTDOWN_X */{"shutdown", EC_NET, (enum ppm_event_flags)(EF_USES_FD | EF_MODIFIES_STATE), 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, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD | EF_MODIFIES_STATE), 3, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"tuple", PT_SOCKTUPLE, PF_NA} } }, /* PPME_SOCKET_SENDMSG_X */{"sendmsg", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD | EF_MODIFIES_STATE), 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SOCKET_SENDMMSG_E */{"sendmmsg", EC_IO_WRITE, EF_NONE, 0}, /* PPME_SOCKET_SENDMMSG_X */{"sendmmsg", EC_IO_WRITE, EF_NONE, 0}, /* PPME_SOCKET_RECVMSG_E */{"recvmsg", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE), 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SOCKET_RECVMSG_X */{"recvmsg", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE), 4, {{"res", PT_ERRNO, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"data", PT_BYTEBUF, PF_NA}, {"tuple", PT_SOCKTUPLE, PF_NA} } }, /* PPME_SOCKET_RECVMMSG_E */{"recvmmsg", EC_IO_READ, EF_NONE, 0}, /* PPME_SOCKET_RECVMMSG_X */{"recvmmsg", EC_IO_READ, EF_NONE, 0}, /* PPME_SOCKET_ACCEPT4_E */{"accept", EC_NET, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE | EF_OLD_VERSION), 1, {{"flags", PT_INT32, PF_HEX} } }, /* PPME_SOCKET_ACCEPT4_X */{"accept", EC_NET, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 0}, /* PPME_SYSCALL_CREAT_X */{"creat", EC_FILE, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 0}, /* PPME_SOCKET_PIPE_X */{"pipe", EC_IPC, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 2, {{"initval", PT_UINT64, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX} } }, /* PPME_SYSCALL_EVENTFD_X */{"eventfd", EC_IPC, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 1, {{"res", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_FUTEX_E */{"futex", EC_IPC, EF_NONE, 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_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_STAT_E */{"stat", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_STAT_X */{"stat", EC_FILE, EF_NONE, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_LSTAT_E */{"lstat", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_LSTAT_X */{"lstat", EC_FILE, EF_NONE, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_FSTAT_E */{"fstat", EC_FILE, EF_USES_FD, 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_FSTAT_X */{"fstat", EC_FILE, EF_USES_FD, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_STAT64_E */{"stat64", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_STAT64_X */{"stat64", EC_FILE, EF_NONE, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_LSTAT64_E */{"lstat64", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_LSTAT64_X */{"lstat64", EC_FILE, EF_NONE, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_FSTAT64_E */{"fstat64", EC_FILE, EF_USES_FD, 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_FSTAT64_X */{"fstat64", EC_FILE, EF_USES_FD, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_EPOLLWAIT_E */{"epoll_wait", EC_WAIT, EF_WAITS, 1, {{"maxevents", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_EPOLLWAIT_X */{"epoll_wait", EC_WAIT, EF_WAITS, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_POLL_E */{"poll", EC_WAIT, EF_WAITS, 2, {{"fds", PT_FDLIST, PF_DEC}, {"timeout", PT_INT64, PF_DEC} } }, /* PPME_SYSCALL_POLL_X */{"poll", EC_WAIT, EF_WAITS, 2, {{"res", PT_ERRNO, PF_DEC}, {"fds", PT_FDLIST, PF_DEC} } }, /* PPME_SYSCALL_SELECT_E */{"select", EC_WAIT, EF_WAITS, 0}, /* PPME_SYSCALL_SELECT_X */{"select", EC_WAIT, EF_WAITS, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_NEWSELECT_E */{"select", EC_WAIT, EF_WAITS, 0}, /* PPME_SYSCALL_NEWSELECT_X */{"select", EC_WAIT, EF_WAITS, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_LSEEK_E */{"lseek", EC_FILE, EF_USES_FD, 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, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_LLSEEK_E */{"llseek", EC_FILE, EF_USES_FD, 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, 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, (enum ppm_event_flags)(EF_USES_FD | EF_MODIFIES_STATE), 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_FCHDIR_X */{"fchdir", EC_FILE, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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_HEX} } }, /* PPME_SYSCALL_OPENAT_X */{"openat", EC_FILE, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_READV_X */{"readv", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 3, {{"res", PT_ERRNO, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_WRITEV_E */{"writev", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD), 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_WRITEV_X */{"writev", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD), 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_PREADV_E */{"preadv", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 2, {{"fd", PT_FD, PF_DEC}, {"pos", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_PREADV_X */{"preadv", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 3, {{"res", PT_ERRNO, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_PWRITEV_E */{"pwritev", EC_IO_WRITE, (enum ppm_event_flags)(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_PWRITEV_X */{"pwritev", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD), 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_DUP_E */{"dup", EC_IO_OTHER, (enum ppm_event_flags)(EF_CREATES_FD | EF_USES_FD | EF_MODIFIES_STATE), 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_DUP_X */{"dup", EC_IO_OTHER, (enum ppm_event_flags)(EF_CREATES_FD | EF_USES_FD | EF_MODIFIES_STATE), 1, {{"res", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_SIGNALFD_E */{"signalfd", EC_SIGNAL, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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, 1, {{"interval", PT_RELTIME, PF_DEC} } }, /* PPME_SYSCALL_NANOSLEEP_X */{"nanosleep", EC_SLEEP, EF_WAITS, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_TIMERFD_CREATE_E */{"timerfd_create", EC_TIME, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 2, {{"clockid", PT_UINT8, PF_DEC}, {"flags", PT_FLAGS8, PF_HEX} } }, /* PPME_SYSCALL_TIMERFD_CREATE_X */{"timerfd_create", EC_TIME, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 1, {{"res", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_INOTIFY_INIT_E */{"inotify_init", EC_IPC, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 1, {{"flags", PT_FLAGS8, PF_HEX} } }, /* PPME_SYSCALL_INOTIFY_INIT_X */{"inotify_init", EC_IPC, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_SKIPPARSERESET | EF_OLD_VERSION), 1, {{"next", PT_PID, PF_DEC} } }, /* PPME_SCHEDSWITCH_1_X */{"NA2", EC_SCHEDULER, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_USES_FD | EF_MODIFIES_STATE), 2, {{"fd", PT_FD, PF_DEC}, {"cmd", PT_FLAGS8, PF_DEC, fcntl_commands} } }, /* PPME_SYSCALL_FCNTL_X */{"fcntl", EC_IO_OTHER, (enum ppm_event_flags)(EF_USES_FD | EF_MODIFIES_STATE), 1, {{"res", PT_FD, PF_DEC} } }, /* PPME_SCHEDSWITCH_6_E */{"switch", EC_SCHEDULER, EF_NONE, 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, (enum ppm_event_flags)(EF_MODIFIES_STATE | EF_OLD_VERSION), 0}, /* PPME_SYSCALL_EXECVE_13_X */{"execve", EC_PROCESS, (enum ppm_event_flags)(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_NONE, 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_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_MMAP2_E */{"mmap2", EC_MEMORY, EF_NONE, 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_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_MUNMAP_E */{"munmap", EC_MEMORY, EF_NONE, 2, {{"addr", PT_UINT64, PF_HEX}, {"length", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_MUNMAP_X */{"munmap", EC_MEMORY, EF_NONE, 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, 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, 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, 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, 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_GETDENTS_X */{"getdents", EC_FILE, EF_USES_FD, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_GETDENTS64_E */{"getdents64", EC_FILE, EF_USES_FD, 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_GETDENTS64_X */{"getdents64", EC_FILE, EF_USES_FD, 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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 0}, /* PPME_SOCKET_ACCEPT_5_X */{"accept", EC_NET, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 1, {{"flags", PT_INT32, PF_HEX} } }, /* PPME_SOCKET_ACCEPT4_5_X */{"accept", EC_NET, (enum ppm_event_flags)(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, 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, 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} }} }; sysdig-0.8.0/driver/flags_table.c000066400000000000000000000302451265472057500167440ustar00rootroot00000000000000/* 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}, { }, }; 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}, {"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}, { }, }; sysdig-0.8.0/driver/main.c000066400000000000000000001632151265472057500154310ustar00rootroot00000000000000/* 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 #include #include #include #include #include #include #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; } 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 ssize_t ppe_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos); void ppm_task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st); #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 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; static const struct file_operations g_ppm_fops = { .open = ppm_open, .release = ppm_release, .mmap = ppm_mmap, .unlocked_ioctl = ppm_ioctl, .owner = THIS_MODULE, }; /* Events file operations */ static const struct file_operations g_ppe_fops = { .write = ppe_write, .owner = THIS_MODULE, }; /* * GLOBALS */ LIST_HEAD(g_consumer_list); static DEFINE_MUTEX(g_consumer_mutex); static bool g_tracepoint_registered; struct cdev *g_ppe_cdev = NULL; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) static struct tracepoint *tp_sys_enter; static struct tracepoint *tp_sys_exit; struct device *g_ppe_dev = NULL; #else struct class_device *g_ppe_dev = NULL; #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 _DEBUG static bool verbose = 1; #else static bool verbose = 0; #endif static unsigned int max_consumers = 5; #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); if (ring->cpu_online) 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; } 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. */ if (ring->cpu_online == false) { 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 */ 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; ring->capture_enabled = false; getnstimeofday(&ring->last_print_time); 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_enter(syscall_enter_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_exit(syscall_exit_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, "signal_switch", tp_signal_deliver); #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 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 #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) tracepoint_synchronize_unregister(); #endif g_tracepoint_registered = false; } 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 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 = kmalloc(memsize, GFP_KERNEL); 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 for_each_process(p) { t = p; do { task_lock(p); #endif if (nentries < pli.max_entries) { cputime_t utime, stime; #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) utime = t->utime; stime = t->stime; #else ppm_task_cputime_adjusted(t, &utime, &stime); #endif proclist_info->entries[nentries].pid = t->pid; proclist_info->entries[nentries].utime = cputime_to_clock_t(utime); proclist_info->entries[nentries].stime = cputime_to_clock_t(stime); } nentries++; #ifdef for_each_process_thread } #else task_unlock(p); } while_each_thread(p, t); } #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: kfree(proclist_info); 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); return -ENODEV; } 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); return -ENODEV; } 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); return -EINVAL; } 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); return -EINVAL; } 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); return -EINVAL; } 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); return -EINVAL; } 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 default: ret = -ENOTTY; goto cleanup_ioctl; } 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); return -ENODEV; } 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; } static ssize_t ppe_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { return count; } /* 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) { 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)) 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)) { atomic_dec(&ring->preempt_count); ring_info->n_preemptions++; put_cpu(); ASSERT(false); 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; 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; } 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 (unlikely(task_thread_info(current)->status & TS_COMPAT)) { 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; #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 (unlikely((task_thread_info(current)->status & TS_COMPAT) && id != __NR_execve)) { 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; #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); } } int __access_remote_vm(struct task_struct *t, struct mm_struct *mm, unsigned long addr, void *buf, int len, int write); TRACEPOINT_PROBE(syscall_procexit_probe, struct task_struct *p) { struct event_data_t event_data; #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; 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; 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 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 err_str_storage; } /* * 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 err_buffer; } 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 err_ring_info; } /* * Initialize the buffer info structure */ 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; atomic_set(&ring->preempt_count, 0); getnstimeofday(&ring->last_print_time); pr_info("CPU buffer initialized, size=%d\n", RING_BUF_SIZE); return 1; err_ring_info: vfree((void *)ring->buffer); ring->buffer = NULL; err_buffer: free_page((unsigned long)ring->str_storage); ring->str_storage = NULL; err_str_storage: return 0; } static void free_ring_buffer(struct ppm_ring_buffer_context *ring) { if (ring->info) vfree(ring->info); if (ring->buffer) vfree((void *)ring->buffer); if (ring->str_storage) free_page((unsigned long)ring->str_storage); } #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 } 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 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) */ /* * This gets called every time a CPU is added or removed */ static int cpu_callback(struct notifier_block *self, unsigned long action, void *hcpu) { long cpu = (long)hcpu; struct ppm_ring_buffer_context *ring; struct ppm_consumer_t *consumer; bool event_recorded = false; struct timespec ts; struct event_data_t event_data; 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; } /* * Based on the action, spit an event in the first available ring */ 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); ring->capture_enabled = false; getnstimeofday(&ts); 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; if (!event_recorded) { record_event_consumer(consumer, PPME_CPU_HOTPLUG_E, UF_NEVER_DROP, &ts, &event_data); event_recorded = true; } } rcu_read_unlock(); } return NOTIFY_DONE; } static struct notifier_block cpu_notifier = { .notifier_call = &cpu_callback, .next = NULL, }; int sysdig_init(void) { dev_t dev; unsigned int cpu; unsigned int num_cpus; int ret; int acrret = 0; 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); */ g_ppe_cdev = cdev_alloc(); if (g_ppe_cdev == NULL) { pr_err("error allocating the device %s\n", PROBE_EVENT_DEVICE_NAME); ret = -ENOMEM; goto init_module_err; } cdev_init(g_ppe_cdev, &g_ppe_fops); if (cdev_add(g_ppe_cdev, MKDEV(g_ppm_major, g_ppm_numdevs), 1) < 0) { pr_err("could not allocate chrdev for %s\n", PROBE_EVENT_DEVICE_NAME); ret = -EFAULT; goto init_module_err; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) g_ppe_dev = device_create( #else g_ppe_dev = class_device_create( #endif g_ppm_class, NULL, MKDEV(g_ppm_major, g_ppm_numdevs), NULL, /* no additional data */ PROBE_EVENT_DEVICE_NAME); if (IS_ERR(g_ppe_dev)) { pr_err("error creating the device for %s\n", PROBE_EVENT_DEVICE_NAME); ret = -EFAULT; goto init_module_err; } /* * Snaplen lookahead initialization */ if (dpi_lookahead_init() != PPM_SUCCESS) { pr_err("initializing lookahead-based snaplen %s\n", PROBE_EVENT_DEVICE_NAME); ret = -EFAULT; goto init_module_err; } /* * Set up our callback in case we get a hotplug even while we are * initializing the cpu structures */ register_cpu_notifier(&cpu_notifier); /* * All ok. Final initalizations. */ g_tracepoint_registered = false; return 0; init_module_err: if (g_ppe_dev != NULL) device_destroy(g_ppm_class, MKDEV(g_ppm_major, g_ppm_numdevs)); if (g_ppe_cdev != NULL) cdev_del(g_ppe_cdev); 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_ppe_dev != NULL) device_destroy(g_ppm_class, MKDEV(g_ppm_major, g_ppm_numdevs)); if (g_ppe_cdev != NULL) cdev_del(g_ppe_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 unregister_cpu_notifier(&cpu_notifier); } 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.8.0/driver/ppm.h000066400000000000000000000064421265472057500153040ustar00rootroot00000000000000/* 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 #define RW_SNAPLEN 80 #define RW_SNAPLEN_EVENT 4096 #define RW_MAX_SNAPLEN (256 * 1024 * 1024) #define DPI_LOOKAHED_SIZE 16 /* * Global enums */ enum syscall_flags { UF_NONE = 0, UF_USED = (1 << 0), UF_NEVER_DROP = (1 << 1), UF_ALWAYS_DROP = (1 << 2), }; /* * 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 #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 #define PPM_PORT_MYSQL 3306 #define PPM_PORT_POSTGRES 5432 #define PPM_PORT_STATSD 8125 sysdig-0.8.0/driver/ppm_compat_unistd_32.h000066400000000000000000000316061265472057500205410ustar00rootroot00000000000000#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.8.0/driver/ppm_cputime.c000066400000000000000000000144521265472057500170250ustar00rootroot00000000000000#include #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 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 #include #include "ppm_ringbuffer.h" #include "ppm_events_public.h" #include "ppm_events.h" #include "ppm.h" #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; } #endif #ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE void task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st) { *ut = p->utime; *st = p->stime; } #else /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */ /* * 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); } 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); } /* * 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); } #endif /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */ #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) */ sysdig-0.8.0/driver/ppm_events.c000066400000000000000000001055371265472057500166700ustar00rootroot00000000000000/* 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 #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 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 (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_op) { if (THIS_MODULE == f.file->f_op->owner) { res = RW_SNAPLEN_EVENT; fdput(f); return res; } fdput(f); } #else struct file* file = fget(args->fd); if (file && file->f_op) { if (THIS_MODULE == file->f_op->owner) { 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) { 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; } /* * Get the current working directory for the current process. * Returns the pointer to the string, which is NOT going to be at the beginning * of buf. * Buf must be at least 1 page in size. */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) char *npm_getcwd(char *buf, unsigned long bufsize) { struct path pwd; char *res; ASSERT(bufsize >= PAGE_SIZE - 1); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || defined CONFIG_VE get_fs_pwd(current->fs, &pwd); #else read_lock(¤t->fs->lock); pwd = current->fs->pwd; path_get(&pwd); read_unlock(¤t->fs->lock); #endif res = d_path(&pwd, buf, bufsize); if (IS_ERR(res)) res = NULL; path_put(&pwd); return res; } #else /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) */ char *npm_getcwd(char *buf, unsigned long bufsize) { struct dentry *dentry; struct vfsmount *mnt; char *res; ASSERT(bufsize >= PAGE_SIZE - 1); read_lock(¤t->fs->lock); mnt = mntget(current->fs->pwdmnt); dentry = dget(current->fs->pwd); read_unlock(¤t->fs->lock); res = d_path(dentry, mnt, buf, bufsize); if (IS_ERR(res)) res = NULL; return res; } #endif 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. */ syscall_get_arguments(current, args->regs, 0, 1, &val); 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. */ syscall_get_arguments(current, args->regs, 0, 1, &val); 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.8.0/driver/ppm_events.h000066400000000000000000000111611265472057500166620ustar00rootroot00000000000000/* 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 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 */ 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 */ }; /* * 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); char *npm_getcwd(char *buf, unsigned long bufsize); 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.8.0/driver/ppm_events_public.h000066400000000000000000001172401265472057500202250ustar00rootroot00000000000000/* 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) /* * 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) /* * 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 /* * 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) /* * 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, }; /** @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, 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, PPM_EVENT_MAX = 268 }; /*@}*/ /* * 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 = 18, 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 */ }; /* * Operators to compare events */ enum ppm_cmp_operator { 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, }; /* * 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_MAX = 35 /* array size */ }; enum ppm_print_format { PF_NA = 0, PF_DEC = 1, /* decimal */ PF_HEX = 2, /* hexadecima */ 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, }; /*! \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) /*! \brief System call description struct. */ struct ppm_syscall_desc { enum ppm_event_category category; /**< System call category. */ char *name; /**< System call name, e.g. 'open'. */ }; 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_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.8.0/driver/ppm_fillers.c000066400000000000000000003646551265472057500170340ustar00rootroot00000000000000/* 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 #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_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 static int f_sys_setns_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_16_E] = {f_sys_empty}, [PPME_SYSCALL_EXECVE_16_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} } } }; #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 int f_sys_open_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; /* * 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, &val); res = val_to_ring(args, open_flags_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Mode * XXX: at this time, mode decoding is not supported. We nonetheless return a value (zero) * so the format of the event is ready for when we'll export the mode in the future. * * syscall_get_arguments(current, args->regs, 2, 1, &val); */ res = val_to_ring(args, 0, 0, false, 0); 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 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) 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(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 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_16_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))) return PPM_FAILURE_INVALID_USER_MEMORY; args->str_storage[args_len - 1] = 0; } else { *args->str_storage = 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)) return args_len; } 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 */ spwd = npm_getcwd(args->str_storage, STR_STORAGE_SIZE - 1); if (spwd == NULL) spwd = ""; args->str_storage[STR_STORAGE_SIZE - 1] = '\0'; 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_16_X) { /* * execve-only parameters */ unsigned long env_len = 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))) return PPM_FAILURE_INVALID_USER_MEMORY; args->str_storage[env_len - 1] = 0; } else { *args->str_storage = 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)) return env_len; } /* * environ */ res = val_to_ring(args, (int64_t)(long)args->str_storage, env_len, 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; 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, &val); res = val_to_ring(args, open_flags_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Mode * XXX: at this time, mode decoding is not supported. We nonetheless return a value (zero) * so the format of the event is ready for when we'll export the mode in the future. * * syscall_get_arguments(current, args->regs, 3, 1, &val); */ res = val_to_ring(args, 0, 0, false, 0); 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 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 case PTRACE_GET_THREAD_AREA: return PPM_PTRACE_GET_THREAD_AREA; case PTRACE_OLDSETOPTIONS: return PPM_PTRACE_OLDSETOPTIONS; #ifdef PTRACE_SETFPXREGS case PTRACE_SETFPXREGS: return PPM_PTRACE_SETFPXREGS; #endif #ifdef PTRACE_GETFPXREGS case PTRACE_GETFPXREGS: return PPM_PTRACE_GETFPXREGS; #endif case PTRACE_SETFPREGS: return PPM_PTRACE_SETFPREGS; case PTRACE_GETFPREGS: return PPM_PTRACE_GETFPREGS; case PTRACE_SETREGS: return PPM_PTRACE_SETREGS; case PTRACE_GETREGS: return PPM_PTRACE_GETREGS; #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); } #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 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.8.0/driver/ppm_ringbuffer.h000066400000000000000000000025311265472057500175100ustar00rootroot00000000000000/* 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.8.0/driver/ppm_syscall.h000066400000000000000000000113341265472057500170320ustar00rootroot00000000000000/* * 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.8.0/driver/syscall_table.c000066400000000000000000002507731265472057500173340ustar00rootroot00000000000000/* 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, 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, PPME_SYSCALL_EXECVE_16_E, PPME_SYSCALL_EXECVE_16_X}, [__NR_clone - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_CLONE_20_E, PPME_SYSCALL_CLONE_20_X}, [__NR_fork - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_FORK_20_E, PPME_SYSCALL_FORK_20_X}, [__NR_vfork - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, 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, PPME_SYSCALL_CHDIR_E, PPME_SYSCALL_CHDIR_X}, [__NR_fchdir - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_FCHDIR_E, PPME_SYSCALL_FCHDIR_X}, [__NR_mkdir - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_MKDIR_E, PPME_SYSCALL_MKDIR_X}, [__NR_rmdir - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_RMDIR_E, PPME_SYSCALL_RMDIR_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, PPME_SYSCALL_DUP_E, PPME_SYSCALL_DUP_X}, [__NR_dup2 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_DUP_E, PPME_SYSCALL_DUP_X}, [__NR_dup3 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, 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}, [__NR_getrlimit - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_GETRLIMIT_E, PPME_SYSCALL_GETRLIMIT_X}, [__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}, [__NR_utime - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__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}, [__NR_alarm - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_GENERIC_E, PPME_GENERIC_X}, [__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, 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, 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, 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, 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 [__NR_mmap - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_MMAP_E, PPME_SYSCALL_MMAP_X}, #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 [__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 }; /* * 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, [__NR_time - SYSCALL_TABLE_ID0] = PPM_SC_TIME, [__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, [__NR_alarm - SYSCALL_TABLE_ID0] = PPM_SC_ALARM, [__NR_fstat - SYSCALL_TABLE_ID0] = PPM_SC_FSTAT, [__NR_pause - SYSCALL_TABLE_ID0] = PPM_SC_PAUSE, [__NR_utime - SYSCALL_TABLE_ID0] = PPM_SC_UTIME, [__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, */ [__NR_mmap - SYSCALL_TABLE_ID0] = PPM_SC_MMAP, [__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, [__NR_getrlimit - SYSCALL_TABLE_ID0] = PPM_SC_GETRLIMIT, /* [__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, 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, PPME_SYSCALL_EXECVE_16_E, PPME_SYSCALL_EXECVE_16_X}, [__NR_ia32_clone - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_CLONE_20_E, PPME_SYSCALL_CLONE_20_X}, [__NR_ia32_fork - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_FORK_20_E, PPME_SYSCALL_FORK_20_X}, [__NR_ia32_vfork - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, 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, PPME_SYSCALL_CHDIR_E, PPME_SYSCALL_CHDIR_X}, [__NR_ia32_fchdir - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_FCHDIR_E, PPME_SYSCALL_FCHDIR_X}, [__NR_ia32_mkdir - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_MKDIR_E, PPME_SYSCALL_MKDIR_X}, [__NR_ia32_rmdir - SYSCALL_TABLE_ID0] = {UF_USED | UF_ALWAYS_DROP, PPME_SYSCALL_RMDIR_E, PPME_SYSCALL_RMDIR_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, PPME_SYSCALL_DUP_E, PPME_SYSCALL_DUP_X}, [__NR_ia32_dup2 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_DUP_E, PPME_SYSCALL_DUP_X}, [__NR_ia32_dup3 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, 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, 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, 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, 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, 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 }; /* * 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.8.0/scripts/000077500000000000000000000000001265472057500145255ustar00rootroot00000000000000sysdig-0.8.0/scripts/CMakeLists.txt000066400000000000000000000006141265472057500172660ustar00rootroot00000000000000configure_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.8.0/scripts/boot2docker-kernel-crawler.py000077500000000000000000000024761265472057500222430ustar00rootroot00000000000000#!/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, 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.8.0/scripts/build-probe-binaries000077500000000000000000000317531265472057500204620ustar00rootroot00000000000000#!/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 BASEDIR=$(pwd) ARCH=$(uname -m) if [ ! -d $BASEDIR/output ]; then mkdir $BASEDIR/output fi function build_probe { if [ "$PROBE_NAME" = "sysdig-probe" ]; then build_sysdig elif [ "$PROBE_NAME" = "sysdigcloud-probe" ]; then build_sysdigcloud else exit 1 fi } function build_sysdig { 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]}] if [ ! -d sysdig ]; then git clone git@github.com:draios/sysdig.git fi cd sysdig git checkout master git pull git checkout $PROBE_VERSION make -C driver clean || true rm -rf build || true mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release -DSYSDIG_VERSION=$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 build_sysdigcloud { 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]}] if [ ! -d sysdig ]; then git clone git@github.com:draios/sysdig.git fi if [ ! -d agent ]; then git clone git@github.com:draios/agent.git fi cd sysdig git checkout master git pull git checkout agent/$PROBE_VERSION make -C driver clean || true rm -rf build || true cd .. cd agent git checkout master git pull git checkout $PROBE_VERSION rm -rf build || true mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release -DAGENT_VERSION=$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 ${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 $KERNEL_URL fi if [ ! -d $DIR_NAME ]; then tar xf $TGZ_NAME cd $DIR_NAME make distclean sed -i "s/^EXTRAVERSION.*/EXTRAVERSION = $EXTRAVERSION/" Makefile cp ../config_orig .config make modules_prepare mv .config ../config cd .. fi HASH=$(md5sum config | cut -d' ' -f1) HASH_ORIG=$(md5sum config_orig | cut -d' ' -f1) cd $BASEDIR export KERNELDIR=$BASEDIR/$COREOS_DIR/$DIR_NAME build_probe } function coreos_build_new { VERSION_URL=$1 VERSION_NUMBER=$2 COREOS_DIR="coreos-"$VERSION_NUMBER if [ ! -d $COREOS_DIR ]; then mkdir $COREOS_DIR fi cd $COREOS_DIR if [ ! -f coreos_developer_container.bin ]; then wget ${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 KERNEL_RELEASE=$(ls config-* | sed s/config-//) HASH_ORIG=$(md5sum config_orig | cut -d' ' -f1) HASH=$HASH_ORIG 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 } 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 $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 -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 $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\-(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 $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 -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 ${URL} dpkg -x ${DEB} ./ fi fi NUM_DEB=$(ls linux-*.deb -1| grep -v kbuild | wc -l) if [[ ${NUM_DEB} -eq 3 ]]; then KBUILD_PACKAGE=$(ls -t ${BASEDIR}/common-dependencies/debian/kbuild | grep ${KERNEL_MAJOR}| head -1) 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} } # # 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 eval $(curl -s ${URL}version.txt) 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 # # Upload modules # aws s3 sync ./output/ s3://download.draios.com/$REPOSITORY_NAME/sysdig-probe-binaries/ --acl public-read echo "Success."sysdig-0.8.0/scripts/completions/000077500000000000000000000000001265472057500170615ustar00rootroot00000000000000sysdig-0.8.0/scripts/completions/bash/000077500000000000000000000000001265472057500177765ustar00rootroot00000000000000sysdig-0.8.0/scripts/completions/bash/sysdig000066400000000000000000000104521265472057500212250ustar00rootroot00000000000000_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 \ -P \ --progress \ -q \ --quiet \ -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.8.0/scripts/completions/zsh/000077500000000000000000000000001265472057500176655ustar00rootroot00000000000000sysdig-0.8.0/scripts/completions/zsh/_sysdig000066400000000000000000000271241265472057500212570ustar00rootroot00000000000000#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[Do not convert port numbers to names]' \ '(-n --numevents)'{-n,--numevents=-}'[Stop capturing after events]:Max events:' \ '(-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"' \ '(-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.8.0/scripts/debian/000077500000000000000000000000001265472057500157475ustar00rootroot00000000000000sysdig-0.8.0/scripts/debian/postinst.in000077500000000000000000000012761265472057500201730ustar00rootroot00000000000000#!/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.8.0/scripts/debian/prerm.in000077500000000000000000000004211265472057500174240ustar00rootroot00000000000000#!/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.8.0/scripts/description.txt000066400000000000000000000006231265472057500176120ustar00rootroot00000000000000Sysdig 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.8.0/scripts/install-sysdig.in000066400000000000000000000112041265472057500200210ustar00rootroot00000000000000#!/bin/bash # # 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 . # 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 http://mirrors.kernel.org/centos/7/extras/x86_64/Packages/epel-release-7-5.noarch.rpm elif [ $VERSION -eq 7 ]; then rpm --quiet -i http://mirrors.kernel.org/fedora-epel/7/x86_64/e/epel-release-7-5.noarch.rpm else rpm --quiet -i http://mirrors.kernel.org/fedora-epel/6/i386/epel-release-6-8.noarch.rpm fi fi echo "* Installing Sysdig public key" rpm --quiet --import https://s3.amazonaws.com/download.draios.com/DRAIOS-GPG-KEY.public echo "* Installing Sysdig repository" curl -s -o /etc/yum.repos.d/draios.repo http://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 else yum -q -y install kernel-devel-$KERNEL_VERSION || kernel_warning fi echo "* Installing Sysdig" yum -q -y install sysdig } 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 Sysdig repository" curl -s -o /etc/apt/sources.list.d/draios.list http://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 Sysdig" apt-get -qq -y install sysdig < /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 sysdig." 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.8.0/scripts/kernel-crawler.py000077500000000000000000000165141265472057500200260ustar00rootroot00000000000000#!/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')]/@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')]/@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')]/@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"] }, { "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"] }, { "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"] } ] } # # 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() 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]]: root = urllib2.urlopen(repo["root"]).read() 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).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.8.0/scripts/rpm/000077500000000000000000000000001265472057500153235ustar00rootroot00000000000000sysdig-0.8.0/scripts/rpm/postinstall000077500000000000000000000011211265472057500176200ustar00rootroot00000000000000dkms 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.8.0/scripts/rpm/preuninstall000077500000000000000000000000751265472057500177730ustar00rootroot00000000000000dkms remove -m sysdig -v %{version} --all --rpm_safe_upgrade sysdig-0.8.0/scripts/sysdig-probe-loader000077500000000000000000000074741265472057500203420ustar00rootroot00000000000000#!/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" else echo "This script must be called as sysdig-probe-loader or sysdigcloud-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.8.0/test/000077500000000000000000000000001265472057500140155ustar00rootroot00000000000000sysdig-0.8.0/test/csysdig_trace_regression.sh000066400000000000000000000065551265472057500214470ustar00rootroot00000000000000#!/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.8.0/test/sysdig_batch_parser.sh000077500000000000000000000021111265472057500203660ustar00rootroot00000000000000#!/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 rm -rf $DIRNAME || true mkdir -p $DIRNAME for f in $TRACESDIR/* do echo "Processing $f" TZ=UTC eval $SYSDIG -N -r $f $ARGS > $DIRNAME/$(basename $f).output done echo Data saved in $DIRNAME diff -r $DIRNAME $REFERENCEDIR rm -rf $DIRNAME sysdig-0.8.0/test/sysdig_trace_regression.sh000077500000000000000000000214021265472057500212730ustar00rootroot00000000000000#!/bin/bash set -eu SCRIPT=$(readlink -f $0) BASEDIR=$(dirname $SCRIPT) SYSDIG=$1 CHISELS=$2 TMPBASE=${4:-$(mktemp -d --tmpdir sysdig.XXXXXXXXXX)} 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 # 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 rm -rf "${TMPBASE}" exit $ret sysdig-0.8.0/userspace/000077500000000000000000000000001265472057500150305ustar00rootroot00000000000000sysdig-0.8.0/userspace/.gitignore000066400000000000000000000000051265472057500170130ustar00rootroot00000000000000test sysdig-0.8.0/userspace/common/000077500000000000000000000000001265472057500163205ustar00rootroot00000000000000sysdig-0.8.0/userspace/common/sysdig_types.h000066400000000000000000000022521265472057500212200ustar00rootroot00000000000000/* 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.8.0/userspace/libscap/000077500000000000000000000000001265472057500164455ustar00rootroot00000000000000sysdig-0.8.0/userspace/libscap/CMakeLists.txt000066400000000000000000000014301265472057500212030ustar00rootroot00000000000000include_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.8.0/userspace/libscap/doxygen/000077500000000000000000000000001265472057500201225ustar00rootroot00000000000000sysdig-0.8.0/userspace/libscap/doxygen/conf.dox000066400000000000000000000176061265472057500215750ustar00rootroot00000000000000# 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.8.0/userspace/libscap/doxygen/footer.html000066400000000000000000000000301265472057500222770ustar00rootroot00000000000000 sysdig-0.8.0/userspace/libscap/doxygen/header.html000066400000000000000000000026111265472057500222400ustar00rootroot00000000000000--- layout: default title: sysdig | libscap ---

$projectname $projectnumber

(Generated by Doxygen)

$projectbrief

$projectbrief
$searchbox
sysdig-0.8.0/userspace/libscap/dynamic_params_table.c000066400000000000000000000014621265472057500227520ustar00rootroot00000000000000/* 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.8.0/userspace/libscap/event_table.c000066400000000000000000001234271265472057500211120ustar00rootroot00000000000000/* 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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 0}, /* PPME_SYSCALL_OPEN_X */{"open", EC_FILE, (enum ppm_event_flags)(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_HEX} } }, /* PPME_SYSCALL_CLOSE_E */{"close", EC_IO_OTHER, (enum ppm_event_flags)(EF_DESTROYS_FD | EF_USES_FD | EF_MODIFIES_STATE), 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_CLOSE_X */{"close", EC_IO_OTHER, (enum ppm_event_flags)(EF_DESTROYS_FD | EF_USES_FD | EF_MODIFIES_STATE), 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_READ_E */{"read", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_READ_X */{"read", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_WRITE_E */{"write", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD), 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_WRITE_X */{"write", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD), 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, (enum ppm_event_flags)(EF_MODIFIES_STATE | EF_OLD_VERSION), 0}, /* PPME_SYSCALL_EXECVE_8_X */{"execve", EC_PROCESS, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_MODIFIES_STATE | EF_OLD_VERSION), 0}, /* PPME_CLONE_11_X */{"clone", EC_PROCESS, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SOCKET_BIND_E */{"bind", EC_NET, EF_USES_FD, 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SOCKET_BIND_X */{"bind", EC_NET, EF_USES_FD, 2, {{"res", PT_ERRNO, PF_DEC}, {"addr", PT_SOCKADDR, PF_NA} } }, /* PPME_SOCKET_CONNECT_E */{"connect", EC_NET, (enum ppm_event_flags)(EF_USES_FD | EF_MODIFIES_STATE), 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SOCKET_CONNECT_X */{"connect", EC_NET, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE | EF_OLD_VERSION), 0}, /* PPME_SOCKET_ACCEPT_X */{"accept", EC_NET, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD), 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_SEND_X */{"send", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD), 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SOCKET_SENDTO_E */{"sendto", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD | EF_MODIFIES_STATE), 3, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"tuple", PT_SOCKTUPLE, PF_NA} } }, /* PPME_SOCKET_SENDTO_X */{"sendto", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD | EF_MODIFIES_STATE), 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SOCKET_RECV_E */{"recv", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SOCKET_RECV_X */{"recv", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SOCKET_RECVFROM_E */{"recvfrom", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE), 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SOCKET_RECVFROM_X */{"recvfrom", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE), 3, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA}, {"tuple", PT_SOCKTUPLE, PF_NA} } }, /* PPME_SOCKET_SHUTDOWN_E */{"shutdown", EC_NET, (enum ppm_event_flags)(EF_USES_FD | EF_MODIFIES_STATE), 2, {{"fd", PT_FD, PF_DEC}, {"how", PT_FLAGS8, PF_HEX, shutdown_how} } }, /* PPME_SOCKET_SHUTDOWN_X */{"shutdown", EC_NET, (enum ppm_event_flags)(EF_USES_FD | EF_MODIFIES_STATE), 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, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD | EF_MODIFIES_STATE), 3, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"tuple", PT_SOCKTUPLE, PF_NA} } }, /* PPME_SOCKET_SENDMSG_X */{"sendmsg", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD | EF_MODIFIES_STATE), 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SOCKET_SENDMMSG_E */{"sendmmsg", EC_IO_WRITE, EF_NONE, 0}, /* PPME_SOCKET_SENDMMSG_X */{"sendmmsg", EC_IO_WRITE, EF_NONE, 0}, /* PPME_SOCKET_RECVMSG_E */{"recvmsg", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE), 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SOCKET_RECVMSG_X */{"recvmsg", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD | EF_MODIFIES_STATE), 4, {{"res", PT_ERRNO, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"data", PT_BYTEBUF, PF_NA}, {"tuple", PT_SOCKTUPLE, PF_NA} } }, /* PPME_SOCKET_RECVMMSG_E */{"recvmmsg", EC_IO_READ, EF_NONE, 0}, /* PPME_SOCKET_RECVMMSG_X */{"recvmmsg", EC_IO_READ, EF_NONE, 0}, /* PPME_SOCKET_ACCEPT4_E */{"accept", EC_NET, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE | EF_OLD_VERSION), 1, {{"flags", PT_INT32, PF_HEX} } }, /* PPME_SOCKET_ACCEPT4_X */{"accept", EC_NET, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 0}, /* PPME_SYSCALL_CREAT_X */{"creat", EC_FILE, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 0}, /* PPME_SOCKET_PIPE_X */{"pipe", EC_IPC, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 2, {{"initval", PT_UINT64, PF_DEC}, {"flags", PT_FLAGS32, PF_HEX} } }, /* PPME_SYSCALL_EVENTFD_X */{"eventfd", EC_IPC, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 1, {{"res", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_FUTEX_E */{"futex", EC_IPC, EF_NONE, 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_NONE, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_STAT_E */{"stat", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_STAT_X */{"stat", EC_FILE, EF_NONE, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_LSTAT_E */{"lstat", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_LSTAT_X */{"lstat", EC_FILE, EF_NONE, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_FSTAT_E */{"fstat", EC_FILE, EF_USES_FD, 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_FSTAT_X */{"fstat", EC_FILE, EF_USES_FD, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_STAT64_E */{"stat64", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_STAT64_X */{"stat64", EC_FILE, EF_NONE, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_LSTAT64_E */{"lstat64", EC_FILE, EF_NONE, 0}, /* PPME_SYSCALL_LSTAT64_X */{"lstat64", EC_FILE, EF_NONE, 2, {{"res", PT_ERRNO, PF_DEC}, {"path", PT_FSPATH, PF_NA} } }, /* PPME_SYSCALL_FSTAT64_E */{"fstat64", EC_FILE, EF_USES_FD, 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_FSTAT64_X */{"fstat64", EC_FILE, EF_USES_FD, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_EPOLLWAIT_E */{"epoll_wait", EC_WAIT, EF_WAITS, 1, {{"maxevents", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_EPOLLWAIT_X */{"epoll_wait", EC_WAIT, EF_WAITS, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_POLL_E */{"poll", EC_WAIT, EF_WAITS, 2, {{"fds", PT_FDLIST, PF_DEC}, {"timeout", PT_INT64, PF_DEC} } }, /* PPME_SYSCALL_POLL_X */{"poll", EC_WAIT, EF_WAITS, 2, {{"res", PT_ERRNO, PF_DEC}, {"fds", PT_FDLIST, PF_DEC} } }, /* PPME_SYSCALL_SELECT_E */{"select", EC_WAIT, EF_WAITS, 0}, /* PPME_SYSCALL_SELECT_X */{"select", EC_WAIT, EF_WAITS, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_NEWSELECT_E */{"select", EC_WAIT, EF_WAITS, 0}, /* PPME_SYSCALL_NEWSELECT_X */{"select", EC_WAIT, EF_WAITS, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_LSEEK_E */{"lseek", EC_FILE, EF_USES_FD, 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, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_LLSEEK_E */{"llseek", EC_FILE, EF_USES_FD, 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, 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, (enum ppm_event_flags)(EF_USES_FD | EF_MODIFIES_STATE), 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_FCHDIR_X */{"fchdir", EC_FILE, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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_HEX} } }, /* PPME_SYSCALL_OPENAT_X */{"openat", EC_FILE, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_READV_X */{"readv", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 3, {{"res", PT_ERRNO, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_WRITEV_E */{"writev", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD), 2, {{"fd", PT_FD, PF_DEC}, {"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_WRITEV_X */{"writev", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD), 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_PREADV_E */{"preadv", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 2, {{"fd", PT_FD, PF_DEC}, {"pos", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_PREADV_X */{"preadv", EC_IO_READ, (enum ppm_event_flags)(EF_USES_FD | EF_READS_FROM_FD), 3, {{"res", PT_ERRNO, PF_DEC}, {"size", PT_UINT32, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_PWRITEV_E */{"pwritev", EC_IO_WRITE, (enum ppm_event_flags)(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_PWRITEV_X */{"pwritev", EC_IO_WRITE, (enum ppm_event_flags)(EF_USES_FD | EF_WRITES_TO_FD), 2, {{"res", PT_ERRNO, PF_DEC}, {"data", PT_BYTEBUF, PF_NA} } }, /* PPME_SYSCALL_DUP_E */{"dup", EC_IO_OTHER, (enum ppm_event_flags)(EF_CREATES_FD | EF_USES_FD | EF_MODIFIES_STATE), 1, {{"fd", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_DUP_X */{"dup", EC_IO_OTHER, (enum ppm_event_flags)(EF_CREATES_FD | EF_USES_FD | EF_MODIFIES_STATE), 1, {{"res", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_SIGNALFD_E */{"signalfd", EC_SIGNAL, (enum ppm_event_flags)(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, (enum ppm_event_flags)(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, 1, {{"interval", PT_RELTIME, PF_DEC} } }, /* PPME_SYSCALL_NANOSLEEP_X */{"nanosleep", EC_SLEEP, EF_WAITS, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_TIMERFD_CREATE_E */{"timerfd_create", EC_TIME, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 2, {{"clockid", PT_UINT8, PF_DEC}, {"flags", PT_FLAGS8, PF_HEX} } }, /* PPME_SYSCALL_TIMERFD_CREATE_X */{"timerfd_create", EC_TIME, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 1, {{"res", PT_FD, PF_DEC} } }, /* PPME_SYSCALL_INOTIFY_INIT_E */{"inotify_init", EC_IPC, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 1, {{"flags", PT_FLAGS8, PF_HEX} } }, /* PPME_SYSCALL_INOTIFY_INIT_X */{"inotify_init", EC_IPC, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_SKIPPARSERESET | EF_OLD_VERSION), 1, {{"next", PT_PID, PF_DEC} } }, /* PPME_SCHEDSWITCH_1_X */{"NA2", EC_SCHEDULER, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_USES_FD | EF_MODIFIES_STATE), 2, {{"fd", PT_FD, PF_DEC}, {"cmd", PT_FLAGS8, PF_DEC, fcntl_commands} } }, /* PPME_SYSCALL_FCNTL_X */{"fcntl", EC_IO_OTHER, (enum ppm_event_flags)(EF_USES_FD | EF_MODIFIES_STATE), 1, {{"res", PT_FD, PF_DEC} } }, /* PPME_SCHEDSWITCH_6_E */{"switch", EC_SCHEDULER, EF_NONE, 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, (enum ppm_event_flags)(EF_MODIFIES_STATE | EF_OLD_VERSION), 0}, /* PPME_SYSCALL_EXECVE_13_X */{"execve", EC_PROCESS, (enum ppm_event_flags)(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_NONE, 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_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_MMAP2_E */{"mmap2", EC_MEMORY, EF_NONE, 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_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_MUNMAP_E */{"munmap", EC_MEMORY, EF_NONE, 2, {{"addr", PT_UINT64, PF_HEX}, {"length", PT_UINT64, PF_DEC} } }, /* PPME_SYSCALL_MUNMAP_X */{"munmap", EC_MEMORY, EF_NONE, 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, 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, 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, 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, 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_GETDENTS_X */{"getdents", EC_FILE, EF_USES_FD, 1, {{"res", PT_ERRNO, PF_DEC} } }, /* PPME_SYSCALL_GETDENTS64_E */{"getdents64", EC_FILE, EF_USES_FD, 1, {{"fd", PT_FD, PF_NA} } }, /* PPME_SYSCALL_GETDENTS64_X */{"getdents64", EC_FILE, EF_USES_FD, 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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 0}, /* PPME_SOCKET_ACCEPT_5_X */{"accept", EC_NET, (enum ppm_event_flags)(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, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 1, {{"flags", PT_INT32, PF_HEX} } }, /* PPME_SOCKET_ACCEPT4_5_X */{"accept", EC_NET, (enum ppm_event_flags)(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, 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, 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} }} }; sysdig-0.8.0/userspace/libscap/examples/000077500000000000000000000000001265472057500202635ustar00rootroot00000000000000sysdig-0.8.0/userspace/libscap/examples/01-open/000077500000000000000000000000001265472057500214425ustar00rootroot00000000000000sysdig-0.8.0/userspace/libscap/examples/01-open/CMakeLists.txt000066400000000000000000000002201265472057500241740ustar00rootroot00000000000000include_directories("../../../common") include_directories("../../") add_executable(scap-open test.c) target_link_libraries(scap-open scap) sysdig-0.8.0/userspace/libscap/examples/01-open/test.c000066400000000000000000000025611265472057500225710ustar00rootroot00000000000000/* 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.8.0/userspace/libscap/examples/02-validatebuffer/000077500000000000000000000000001265472057500234655ustar00rootroot00000000000000sysdig-0.8.0/userspace/libscap/examples/02-validatebuffer/CMakeLists.txt000066400000000000000000000002431265472057500262240ustar00rootroot00000000000000include_directories("../../../common") include_directories("../..") add_executable(scap-validatebuffer test.c) target_link_libraries(scap-validatebuffer scap) sysdig-0.8.0/userspace/libscap/examples/02-validatebuffer/test.c000066400000000000000000000144051265472057500246140ustar00rootroot00000000000000/* 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.8.0/userspace/libscap/flags_table.c000066400000000000000000000305701265472057500210610ustar00rootroot00000000000000/* 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}, {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}, {"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}, }; sysdig-0.8.0/userspace/libscap/scap-int.h000066400000000000000000000170241265472057500203400ustar00rootroot00000000000000/* 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_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; }; 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, gzFile f); // 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 interfaces 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 _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;\ } // // 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.8.0/userspace/libscap/scap.c000066400000000000000000000663131265472057500175500ustar00rootroot00000000000000/* 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 #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 static uint32_t get_max_consumers() { uint32_t max; FILE *pfile = fopen("/sys/module/sysdig_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; } char* scap_getlasterr(scap_t* handle) { return handle->m_lasterr; } scap_t* scap_open_live_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 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)); // // 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; // // 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/sysdig_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; } // // 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(const char* fname, char *error, proc_entry_callback proc_callback, void* proc_callback_context, bool import_users) { 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_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_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; } // // Open the file // handle->m_file = gzopen(fname, "rb"); if(handle->m_file == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "can't open file %s", fname); scap_close(handle); return NULL; } // // Validate the file and load the non-event blocks // if(scap_read_init(handle, handle->m_file) != SCAP_SUCCESS) { snprintf(error, SCAP_LASTERR_SIZE, "%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) { return scap_open_offline_int(fname, error, NULL, NULL, true); } scap_t* scap_open_live(char *error) { return scap_open_live_int(error, NULL, NULL, true); } scap_t* scap_open(scap_open_args args, char *error) { if(args.fname != NULL) { return scap_open_offline_int(args.fname, error, args.proc_callback, args.proc_callback_context, args.import_users); } else { return scap_open_live_int(error, args.proc_callback, args.proc_callback_context, args.import_users); } } void scap_close(scap_t* handle) { if(handle->m_file) { gzclose(handle->m_file); } else { #if defined(HAS_CAPTURE) uint32_t j; ASSERT(handle->m_file == 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 // if(handle->m_devs != NULL) { 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 } int32_t scap_next(scap_t* handle, OUT scap_evt** pevent, OUT uint16_t* pcpuid) { int32_t res; if(handle->m_file) { res = scap_next_offline(handle, pevent, pcpuid); } else { res = scap_next_live(handle, pevent, pcpuid); } 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_preemptions = 0; for(j = 0; j < handle->m_ndevs; j++) { stats->n_evts += handle->m_devs[j].m_bufinfo->n_evts; 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_file) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "cannot stop offline 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_file) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "cannot start offline captures"); 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_file) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "dropping mode not supported on offline captures"); ASSERT(false); return SCAP_FAILURE; } if(handle->m_ndevs) { if(ioctl(handle->m_devs[0].m_fd, request, sampling_ratio)) { 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_file) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "setting snaplen not supported on offline captures"); 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_file == NULL) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_get_readfile_offset only works on trace files"); 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_file) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "manipulating eventmasks not supported on offline captures"); 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_file) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "setting snaplen not supported on offline captures"); 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_file) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "setting snaplen not supported on offline captures"); 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_file) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "scap_get_threadlist_from_driver not supported on offline captures"); 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; } sysdig-0.8.0/userspace/libscap/scap.def000066400000000000000000000017451265472057500200620ustar00rootroot00000000000000LIBRARY scap EXPORTS scap_open_live scap_open_offline 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_close scap_dump_get_offset scap_dump_flush scap_dump 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.8.0/userspace/libscap/scap.h000066400000000000000000000627721265472057500175620ustar00rootroot00000000000000/* 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 // // 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_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_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 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. char comm[SCAP_MAX_PATH_SIZE]; ///< Command name (e.g. "top") char exe[SCAP_MAX_PATH_SIZE]; ///< argv[0] (e.g. "sshd: user@pts/4") char args[SCAP_MAX_ARGS_SIZE]; ///< Command line arguments (e.g. "-d1") uint16_t args_len; ///< Command line arguments length char env[SCAP_MAX_ENV_SIZE]; ///< Environment uint16_t env_len; ///< Environment length char cwd[SCAP_MAX_PATH_SIZE]; ///< 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]; int filtered_out; ///< nonzero if this entry should not be saved to file scap_fdinfo* fdlist; ///< The fd table for this process 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 struct scap_open_args { 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. }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_dump_flags; typedef struct scap_dumper scap_dumper_t; /*@}*/ /////////////////////////////////////////////////////////////////////////////// // 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 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 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 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 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); 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_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); #ifdef __cplusplus } #endif sysdig-0.8.0/userspace/libscap/scap.vcxproj000066400000000000000000000146701265472057500210200ustar00rootroot00000000000000 Debug Win32 Release Win32 {A45C5520-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;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.8.0/userspace/libscap/scap.vcxproj.filters000066400000000000000000000051511265472057500224610ustar00rootroot00000000000000 {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.8.0/userspace/libscap/scap_event.c000066400000000000000000000032201265472057500207350ustar00rootroot00000000000000/* 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; } 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.8.0/userspace/libscap/scap_fds.c000066400000000000000000001105161265472057500203770ustar00rootroot00000000000000/* 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 "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: case SCAP_FD_DIRECTORY: break; case SCAP_FD_UNSUPPORTED: 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_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: 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; } // // Write the given fd info to disk // int32_t scap_fd_write_to_disk(scap_t *handle, scap_fdinfo *fdi, gzFile f) { uint8_t type = (uint8_t)fdi->type; uint16_t stlen; if(gzwrite(f, &(fdi->fd), sizeof(uint64_t)) != sizeof(uint64_t) || gzwrite(f, &(fdi->ino), sizeof(uint64_t)) != sizeof(uint64_t) || gzwrite(f, &(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(gzwrite(f, &(fdi->info.ipv4info.sip), sizeof(uint32_t)) != sizeof(uint32_t) || gzwrite(f, &(fdi->info.ipv4info.dip), sizeof(uint32_t)) != sizeof(uint32_t) || gzwrite(f, &(fdi->info.ipv4info.sport), sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, &(fdi->info.ipv4info.dport), sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, &(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(gzwrite(f, &(fdi->info.ipv4serverinfo.ip), sizeof(uint32_t)) != sizeof(uint32_t) || gzwrite(f, &(fdi->info.ipv4serverinfo.port), sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, &(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(gzwrite(f, (char*)fdi->info.ipv6info.sip, sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4 || gzwrite(f, (char*)fdi->info.ipv6info.dip, sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4 || gzwrite(f, &(fdi->info.ipv6info.sport), sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, &(fdi->info.ipv6info.dport), sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, &(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(gzwrite(f, &(fdi->info.ipv6serverinfo.ip), sizeof(uint32_t) * 4) != sizeof(uint32_t) * 4 || gzwrite(f, &(fdi->info.ipv6serverinfo.port), sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, &(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(gzwrite(f, &(fdi->info.unix_socket_info.source), sizeof(uint64_t)) != sizeof(uint64_t) || gzwrite(f, &(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(gzwrite(f, &stlen, sizeof(uint16_t)) != sizeof(uint16_t) || (stlen > 0 && gzwrite(f, 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_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: stlen = (uint16_t)strnlen(fdi->info.fname, SCAP_MAX_PATH_SIZE); if(gzwrite(f, &stlen, sizeof(uint16_t)) != sizeof(uint16_t) || (stlen > 0 && gzwrite(f, fdi->info.fname, stlen) != stlen)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fi6)"); return SCAP_FAILURE; } break; default: 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_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: res = scap_fd_read_fname_from_disk(handle, fdi->info.fname,nbytes,f); 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[1024]; ssize_t r; uint64_t ino; struct stat sb; r = readlink(fname, link_name, 1024); 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); } int32_t scap_fd_handle_regular_file(scap_t *handle, char *fname, scap_threadinfo *tinfo, scap_fdinfo *fdi, char *error) { char link_name[1024]; ssize_t r; r = readlink(fname, link_name, 1024); 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 { 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[1024]; 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, 1024); 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[1024]; 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)) { // 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(line, delimiters); 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(NULL, delimiters); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 3. Protocol token = strtok(NULL, delimiters); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 4. Flags token = strtok(NULL, delimiters); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 5. Type token = strtok(NULL, delimiters); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 6. St token = strtok(NULL, delimiters); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } // 7. Inode token = strtok(NULL, delimiters); if(token == NULL) { ASSERT(false); free(fdinfo); continue; } sscanf(token, "%"PRIu64, &(fdinfo->ino)); // 8. Path token = strtok(NULL, delimiters); 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; } 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), "%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[1024]; char f_name[1024]; char link_name[1024]; struct stat sb; uint64_t fd; scap_fdinfo *fdi = NULL; uint64_t net_ns; ssize_t r; snprintf(fd_dir_name, 1024, "%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) { fdi = NULL; snprintf(f_name, 1024, "%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; } 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); if(SCAP_FAILURE == res) { break; } fdi->ino = sb.st_ino; res = scap_fd_handle_regular_file(handle, f_name, tinfo, fdi, 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, 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, error); break; } if(handle->m_proc_callback != NULL) { if(fdi) { scap_fd_free_fdinfo(&fdi); } } if(SCAP_SUCCESS != res) { break; } } 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.8.0/userspace/libscap/scap_iflist.c000066400000000000000000000141171265472057500211150ustar00rootroot00000000000000/* 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.8.0/userspace/libscap/scap_procs.c000066400000000000000000000470031265472057500207510ustar00rootroot00000000000000/* 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 #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; uint32_t vmsize_kb; uint32_t vmrss_kb; uint32_t vmswap_kb; uint64_t pfmajor; uint64_t pfminor; char line[512]; char tmpc; char* s; tinfo->uid = (uint32_t)-1; tinfo->ptid = (uint32_t)-1LL; tinfo->vmsize_kb = 0; tinfo->vmrss_kb = 0; tinfo->vmswap_kb = 0; tinfo->pfmajor = 0; tinfo->pfminor = 0; tinfo->filtered_out = 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); } } if(nfound == 6) { break; } } ASSERT(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 " %" PRId64 " %" PRId64 " %" PRId64 " %" PRId64 " %" PRId64 " %" PRId64, &tmpc, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &pfminor, &tmp, &pfmajor) != 10) { ASSERT(false); fclose(f); return SCAP_FAILURE; } tinfo->pfmajor = pfmajor; tinfo->pfminor = pfminor; 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; // id token = strtok(line, ":"); if(token == NULL) { ASSERT(false); fclose(f); return SCAP_FAILURE; } // subsys subsys_list = strtok(NULL, ":"); if(subsys_list == NULL) { ASSERT(false); fclose(f); return SCAP_FAILURE; } // transient cgroup if(strncmp(subsys_list, "name=", sizeof("name=") - 1) == 0) { continue; } // cgroup cgroup = strtok(NULL, ":"); if(cgroup == NULL) { ASSERT(false); fclose(f); return SCAP_FAILURE; } // remove the \n cgroup[strlen(cgroup) - 1] = 0; while((token = strtok(subsys_list, ",")) != 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_file) { ASSERT(false); 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_file) { ASSERT(false); 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_file) { 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[256]; 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; 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); } } // // This is a real user level process. Allocate the procinfo structure. // tinfo = (scap_threadinfo*)malloc(sizeof(scap_threadinfo)); if(tinfo == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "process table allocation error (1)"); return SCAP_FAILURE; } memset(tinfo, 0, sizeof(scap_threadinfo)); 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; } // // 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; } if(scap_get_vtid(handle, tinfo->tid, &tinfo->vtid) == SCAP_FAILURE) { tinfo->vtid = tinfo->tid; } if(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 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 // 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_file) { 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_file) { ASSERT(false); 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 } void scap_proc_free(scap_t* handle, struct scap_threadinfo* proc) { scap_fd_free_proc_fd_table(handle, proc); free(proc); } // // 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.8.0/userspace/libscap/scap_savefile.c000077500000000000000000001424321265472057500214260ustar00rootroot00000000000000/* 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 /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// #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(gzFile f, uint32_t blocklen) { int32_t val = 0; uint32_t bytestowrite = scap_normalize_block_len(blocklen) - blocklen; if(gzwrite(f, &val, bytestowrite) == bytestowrite) { return SCAP_SUCCESS; } else { return SCAP_FAILURE; } } static int32_t scap_write_proc_fds(scap_t *handle, struct scap_threadinfo *tinfo, gzFile f) { 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) { 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(gzwrite(f, &bh, sizeof(bh)) != sizeof(bh)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (fd1)"); return SCAP_FAILURE; } // // Write the tid // if(gzwrite(f, &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, f) != SCAP_SUCCESS) { return SCAP_FAILURE; } } } // // Add the padding // if(scap_write_padding(f, 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(gzwrite(f, &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, gzFile f) { 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, f); if(res != SCAP_SUCCESS) { return res; } } } return SCAP_SUCCESS; } // // Write the process list block // static int32_t scap_write_proclist(scap_t *handle, gzFile f) { block_header bh; uint32_t bt; uint32_t totlen = 0; struct scap_threadinfo *tinfo; struct scap_threadinfo *ttinfo; uint16_t commlen; uint16_t exelen; uint16_t argslen; uint16_t cwdlen; uint16_t rootlen; // // First pass pass of the table to calculate the length // HASH_ITER(hh, handle->m_proclist, tinfo, ttinfo) { if(!tinfo->filtered_out) { totlen += (uint32_t) (sizeof(uint64_t) + // tid sizeof(uint64_t) + // pid sizeof(uint64_t) + // ptid 2 + strnlen(tinfo->comm, SCAP_MAX_PATH_SIZE) + 2 + strnlen(tinfo->exe, 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)); } } // // Create the block // bh.block_type = PL_BLOCK_TYPE_V5; bh.block_total_length = scap_normalize_block_len(sizeof(block_header) + totlen + 4); if(gzwrite(f, &bh, sizeof(bh)) != sizeof(bh)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (1)"); return SCAP_FAILURE; } // // Second pass pass of the table to dump it // HASH_ITER(hh, handle->m_proclist, tinfo, ttinfo) { if(tinfo->filtered_out) { continue; } commlen = (uint16_t)strnlen(tinfo->comm, SCAP_MAX_PATH_SIZE); exelen = (uint16_t)strnlen(tinfo->exe, 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(gzwrite(f, &(tinfo->tid), sizeof(uint64_t)) != sizeof(uint64_t) || gzwrite(f, &(tinfo->pid), sizeof(uint64_t)) != sizeof(uint64_t) || gzwrite(f, &(tinfo->ptid), sizeof(uint64_t)) != sizeof(uint64_t) || gzwrite(f, &commlen, sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, tinfo->comm, commlen) != commlen || gzwrite(f, &exelen, sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, tinfo->exe, exelen) != exelen || gzwrite(f, &argslen, sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, tinfo->args, argslen) != argslen || gzwrite(f, &cwdlen, sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, tinfo->cwd, cwdlen) != cwdlen || gzwrite(f, &(tinfo->fdlimit), sizeof(uint64_t)) != sizeof(uint64_t) || gzwrite(f, &(tinfo->flags), sizeof(uint32_t)) != sizeof(uint32_t) || gzwrite(f, &(tinfo->uid), sizeof(uint32_t)) != sizeof(uint32_t) || gzwrite(f, &(tinfo->gid), sizeof(uint32_t)) != sizeof(uint32_t) || gzwrite(f, &(tinfo->vmsize_kb), sizeof(uint32_t)) != sizeof(uint32_t) || gzwrite(f, &(tinfo->vmrss_kb), sizeof(uint32_t)) != sizeof(uint32_t) || gzwrite(f, &(tinfo->vmswap_kb), sizeof(uint32_t)) != sizeof(uint32_t) || gzwrite(f, &(tinfo->pfmajor), sizeof(uint64_t)) != sizeof(uint64_t) || gzwrite(f, &(tinfo->pfminor), sizeof(uint64_t)) != sizeof(uint64_t) || gzwrite(f, &(tinfo->env_len), sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, tinfo->env, tinfo->env_len) != tinfo->env_len || gzwrite(f, &(tinfo->vtid), sizeof(int64_t)) != sizeof(int64_t) || gzwrite(f, &(tinfo->vpid), sizeof(int64_t)) != sizeof(int64_t) || gzwrite(f, &(tinfo->cgroups_len), sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, tinfo->cgroups, tinfo->cgroups_len) != tinfo->cgroups_len || gzwrite(f, &rootlen, sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, tinfo->root, rootlen) != rootlen) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (2)"); return SCAP_FAILURE; } } // // Blocks need to be 4-byte padded // if(scap_write_padding(f, 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(gzwrite(f, &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 machine info block // static int32_t scap_write_machine_info(scap_t *handle, gzFile f) { 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(gzwrite(f, &bh, sizeof(bh)) != sizeof(bh) || gzwrite(f, &handle->m_machine_info, sizeof(handle->m_machine_info)) != sizeof(handle->m_machine_info) || gzwrite(f, &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, gzFile f) { 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(gzwrite(f, &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(gzwrite(f, 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(gzwrite(f, 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(f, 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(gzwrite(f, &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, gzFile f) { 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(gzwrite(f, &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(gzwrite(f, &(type), sizeof(type)) != sizeof(type) || gzwrite(f, &(info->uid), sizeof(info->uid)) != sizeof(info->uid) || gzwrite(f, &(info->gid), sizeof(info->gid)) != sizeof(info->gid) || gzwrite(f, &namelen, sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, info->name, namelen) != namelen || gzwrite(f, &homedirlen, sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, info->homedir, homedirlen) != homedirlen || gzwrite(f, &shelllen, sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, 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(gzwrite(f, &(type), sizeof(type)) != sizeof(type) || gzwrite(f, &(info->gid), sizeof(info->gid)) != sizeof(info->gid) || gzwrite(f, &namelen, sizeof(uint16_t)) != sizeof(uint16_t) || gzwrite(f, 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(f, 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(gzwrite(f, &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 // static scap_dumper_t *scap_setup_dump(scap_t *handle, gzFile f, 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(gzwrite(f, &bh, sizeof(bh)) != sizeof(bh) || gzwrite(f, &sh, sizeof(sh)) != sizeof(sh) || gzwrite(f, &bt, sizeof(bt)) != sizeof(bt)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file %s (5)", fname); return NULL; } // // 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 NULL; } handle->m_proc_callback = tcb; } #endif // // Write the machine info // if(scap_write_machine_info(handle, f) != SCAP_SUCCESS) { return NULL; } // // Write the interface list // if(scap_write_iflist(handle, f) != SCAP_SUCCESS) { return NULL; } // // Write the user list // if(scap_write_userlist(handle, f) != SCAP_SUCCESS) { return NULL; } // // Write the process list // if(scap_write_proclist(handle, f) != SCAP_SUCCESS) { return NULL; } // // Write the fd lists // if(scap_write_fdlist(handle, f) != SCAP_SUCCESS) { return NULL; } // // 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_dumper_t *)f; } // // 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_setup_dump(handle, f, fname); } // // Close a "savefile" opened with scap_dump_open // void scap_dump_close(scap_dumper_t *d) { gzclose((gzFile)d); } // // Return the current size of a tracefile // int64_t scap_dump_get_offset(scap_dumper_t *d) { return gzoffset((gzFile)d); } void scap_dump_flush(scap_dumper_t *d) { gzflush((gzFile)d, 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; gzFile f = (gzFile)d; 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(gzwrite(f, &bh, sizeof(bh)) != sizeof(bh) || gzwrite(f, &cpuid, sizeof(cpuid)) != sizeof(cpuid) || gzwrite(f, e, e->len) != e->len || scap_write_padding(f, sizeof(cpuid) + e->len) != SCAP_SUCCESS || gzwrite(f, &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(gzwrite(f, &bh, sizeof(bh)) != sizeof(bh) || gzwrite(f, &cpuid, sizeof(cpuid)) != sizeof(cpuid) || gzwrite(f, &flags, sizeof(flags)) != sizeof(flags) || gzwrite(f, e, e->len) != e->len || scap_write_padding(f, sizeof(cpuid) + e->len) != SCAP_SUCCESS || gzwrite(f, &bt, sizeof(bt)) != sizeof(bt)) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "error writing to file (6)"); 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; 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; // // 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; // // 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: // // 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) { // // 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) { // // 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) { 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); 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(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); 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); 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_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); return SCAP_FAILURE; } 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); 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.8.0/userspace/libscap/scap_savefile.h000066400000000000000000000116751265472057500214340ustar00rootroot00000000000000/* 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 /////////////////////////////////////////////////////////////////////////////// // 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.8.0/userspace/libscap/scap_userlist.c000066400000000000000000000103141265472057500214700ustar00rootroot00000000000000/* 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 interfaces 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 interfaces 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; } // // 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); free(handle->m_userlist->users); 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.8.0/userspace/libscap/settings.h000066400000000000000000000002731265472057500204600ustar00rootroot00000000000000// // 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 sysdig-0.8.0/userspace/libscap/stdint_win.h000066400000000000000000000170601265472057500210040ustar00rootroot00000000000000// 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.8.0/userspace/libscap/syscall_info_table.c000066400000000000000000000506731265472057500224600ustar00rootroot00000000000000/* 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" /* * SYSCALL INFO TABLE */ const struct ppm_syscall_desc g_syscall_info_table[PPM_SC_MAX] = { /*dummy*/ { EC_OTHER, "" }, /*PPM_SC_RESTART_SYSCALL*/ { EC_SYSTEM, "restart_syscall" }, /*PPM_SC_EXIT*/ { EC_PROCESS, "exit" }, /*PPM_SC_READ*/ { EC_IO_READ, "read" }, /*PPM_SC_WRITE*/ { EC_IO_WRITE, "write" }, /*PPM_SC_OPEN*/ { EC_FILE, "open" }, /*PPM_SC_CLOSE*/ { EC_FILE, "close" }, /*PPM_SC_CREAT*/ { EC_FILE, "creat" }, /*PPM_SC_LINK*/ { EC_FILE, "link" }, /*PPM_SC_UNLINK*/ { EC_FILE, "unlink" }, /*PPM_SC_CHDIR*/ { EC_FILE, "chdir" }, /*PPM_SC_TIME*/ { EC_TIME, "time" }, /*PPM_SC_MKNOD*/ { EC_FILE, "mknod" }, /*PPM_SC_CHMOD*/ { EC_FILE, "chmod" }, /*PPM_SC_STAT*/ { EC_FILE, "stat" }, /*PPM_SC_LSEEK*/ { EC_FILE, "lseek" }, /*PPM_SC_GETPID*/ { EC_PROCESS, "getpid" }, /*PPM_SC_MOUNT*/ { EC_FILE, "mount" }, /*PPM_SC_PTRACE*/ { EC_OTHER, "ptrace" }, /*PPM_SC_ALARM*/ { EC_TIME, "alarm" }, /*PPM_SC_FSTAT*/ { EC_FILE, "fstat" }, /*PPM_SC_PAUSE*/ { EC_WAIT, "pause" }, /* WAIT UNTIL A SIGNAL ARRIVES */ /*PPM_SC_UTIME*/ { EC_TIME, "utime" }, /*PPM_SC_ACCESS*/ { EC_FILE, "access" }, /* checks whether the calling process can access the file pathname */ /*PPM_SC_SYNC*/ { EC_IO_OTHER, "sync" }, /* causes all buffered modifications to file metadata and data to be written to the underlying file systems. */ /*PPM_SC_KILL*/ { EC_IPC, "kill" }, /*PPM_SC_RENAME*/ { EC_FILE, "rename" }, /*PPM_SC_MKDIR*/ { EC_FILE, "mkdir" }, /*PPM_SC_RMDIR*/ { EC_FILE, "rmdir" }, /*PPM_SC_DUP*/ { EC_IO_OTHER, "dup" }, /*PPM_SC_PIPE*/ { EC_IPC, "pipe" }, /*PPM_SC_TIMES*/ { EC_TIME, "times" }, /*PPM_SC_BRK*/ { EC_MEMORY, "brk" }, /*PPM_SC_ACCT*/ { EC_PROCESS, "acct" }, /*PPM_SC_IOCTL*/ { EC_IO_OTHER, "ioctl" }, /*PPM_SC_FCNTL*/ { EC_WAIT, "fcntl" }, /*PPM_SC_SETPGID*/ { EC_PROCESS, "setpgid" }, /*PPM_SC_UMASK*/ { EC_PROCESS, "umask" }, /* sets the calling process's file mode creation mask */ /*PPM_SC_CHROOT*/ { EC_IPC, "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, "ustat" }, /* returns information about a mounted file system. */ /*PPM_SC_DUP2*/ { EC_IO_OTHER, "dup2" }, /*PPM_SC_GETPPID*/ { EC_PROCESS, "getppid" }, /*PPM_SC_GETPGRP*/ { EC_PROCESS, "getpgrp" }, /*PPM_SC_SETSID*/ { EC_PROCESS, "setsid" }, /* creates a session and sets the process group ID */ /*PPM_SC_SETHOSTNAME*/ { EC_SYSTEM, "sethostname" }, /*PPM_SC_SETRLIMIT*/ { EC_PROCESS, "setrlimit" }, /* get/set resource (CPU, FDs, memory...) limits */ /*PPM_SC_GETRUSAGE*/ { EC_PROCESS, "getrusage" }, /* returns resource usage measures for who */ /*PPM_SC_GETTIMEOFDAY*/ { EC_TIME, "gettimeofday" }, /*PPM_SC_SETTIMEOFDAY*/ { EC_TIME, "settimeofday" }, /*PPM_SC_SYMLINK*/ { EC_FILE, "symlink" }, /*PPM_SC_LSTAT*/ { EC_FILE, "lstat" }, /*PPM_SC_READLINK*/ { EC_FILE, "readlink" }, /*PPM_SC_USELIB*/ { EC_PROCESS, "uselib" }, /* load shared library */ /*PPM_SC_SWAPON*/ { EC_PROCESS, "swapon" }, /* start/stop swapping to file/device */ /*PPM_SC_REBOOT*/ { EC_SYSTEM, "reboot" }, /*PPM_SC_MMAP*/ { EC_FILE, "mmap" }, /*PPM_SC_MUNMAP*/ { EC_FILE, "munmap" }, /*PPM_SC_TRUNCATE*/ { EC_FILE, "truncate" }, /* truncate a file to a specified length */ /*PPM_SC_FTRUNCATE*/ { EC_FILE, "ftruncate" }, /* truncate a file to a specified length */ /*PPM_SC_FCHMOD*/ { EC_FILE, "fchmod" }, /*PPM_SC_GETPRIORITY*/ { EC_PROCESS, "getpriority" }, /* get/set program scheduling priority */ /*PPM_SC_SETPRIORITY*/ { EC_PROCESS, "setpriority" }, /* get/set program scheduling priority */ /*PPM_SC_STATFS*/ { EC_FILE, "statfs" }, /* returns information about a mounted file system */ /*PPM_SC_FSTATFS*/ { EC_FILE, "fstatfs" }, /* returns information about a mounted file system */ /*PPM_SC_SYSLOG*/ { EC_SYSTEM, "syslog" }, /* read and/or clear kernel message ring buffer; set console_loglevel */ /*PPM_SC_SETITIMER*/ { EC_TIME, "setitimer" }, /*PPM_SC_GETITIMER*/ { EC_TIME, "getitimer" }, /*PPM_SC_UNAME*/ { EC_SYSTEM, "uname" }, /* get name and information about current kernel */ /*PPM_SC_VHANGUP*/ { EC_OTHER , "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, "wait4" }, /* OBSOLETE */ /*PPM_SC_SWAPOFF*/ { EC_SYSTEM, "swapoff" }, /* start/stop swapping to file/device */ /*PPM_SC_SYSINFO*/ { EC_SYSTEM, "sysinfo" }, /* returns information on overall system statistics */ /*PPM_SC_FSYNC*/ { EC_IO_OTHER, "fsync" }, /* sync file content */ /*PPM_SC_SETDOMAINNAME*/ { EC_SYSTEM, "setdomainname" }, /*PPM_SC_ADJTIMEX*/ { EC_SYSTEM, "adjtimex" }, /* tune kernel clock */ /*PPM_SC_MPROTECT*/ { EC_MEMORY, "mprotect" }, /* set protection on a region of memory */ /*PPM_SC_INIT_MODULE*/ { EC_SYSTEM, "init_module" }, /* load a kernel module */ /*PPM_SC_DELETE_MODULE*/ { EC_SYSTEM, "delete_module" }, /*PPM_SC_QUOTACTL*/ { EC_SYSTEM, "quotactl" }, /*PPM_SC_GETPGID*/ { EC_PROCESS, "getpgid" }, /*PPM_SC_FCHDIR*/ { EC_FILE, "fchdir" }, /*PPM_SC_SYSFS*/ { EC_SYSTEM, "sysfs" }, /* get file system type information */ /*PPM_SC_PERSONALITY*/ { EC_PROCESS, "personality" }, /* set the process execution domain */ /*PPM_SC_GETDENTS*/ { EC_FILE, "getdents" }, /* get directory entries */ /*PPM_SC_SELECT*/ { EC_WAIT, "select" }, /*PPM_SC_FLOCK*/ { EC_FILE, "flock" }, /* apply or remove an advisory lock on an open file */ /*PPM_SC_MSYNC*/ { EC_IO_OTHER, "msync" }, /* synchronize a file with a memory map */ /*PPM_SC_READV*/ { EC_IO_READ, "readv" }, /*PPM_SC_WRITEV*/ { EC_IO_WRITE, "writev" }, /*PPM_SC_GETSID*/ { EC_PROCESS, "getsid" }, /* returns the session ID of the calling process */ /*PPM_SC_FDATASYNC*/ { EC_IO_OTHER, "fdatasync" }, /* synchronize a file's in-core state with storage device */ /*PPM_SC_MLOCK*/ { EC_MEMORY, "mlock" }, /* mlock() and mlockall() respectively lock part or all of the calling process's virtual address space into RAM */ /*PPM_SC_MUNLOCK*/ { EC_MEMORY, "munlock" }, /* mlock() and mlockall() respectively lock part or all of the calling process's virtual address space into RAM */ /*PPM_SC_MLOCKALL*/ { EC_MEMORY, "mlockall" }, /* mlock() and mlockall() respectively lock part or all of the calling process's virtual address space into RAM */ /*PPM_SC_MUNLOCKALL*/ { EC_MEMORY, "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, "sched_setparam" }, /*PPM_SC_SCHED_GETPARAM*/ { EC_PROCESS, "sched_getparam" }, /*PPM_SC_SCHED_SETSCHEDULER*/ { EC_PROCESS, "sched_setscheduler" }, /*PPM_SC_SCHED_GETSCHEDULER*/ { EC_PROCESS, "sched_getscheduler" }, /*PPM_SC_SCHED_YIELD*/ { EC_SLEEP, "sched_yield" }, /*PPM_SC_SCHED_GET_PRIORITY_MAX*/ { EC_PROCESS, "sched_get_priority_max" }, /*PPM_SC_SCHED_GET_PRIORITY_MIN*/ { EC_PROCESS, "sched_get_priority_min" }, /*PPM_SC_SCHED_RR_GET_INTERVAL*/ { EC_PROCESS, "sched_rr_get_interval" }, /*PPM_SC_NANOSLEEP*/ { EC_SLEEP, "nanosleep" }, /*PPM_SC_MREMAP*/ { EC_FILE, "mremap" }, /*PPM_SC_POLL*/ { EC_WAIT, "poll" }, /*PPM_SC_PRCTL*/ { EC_PROCESS, "prctl" }, /* operations on a process */ /*PPM_SC_RT_SIGACTION*/ { EC_SIGNAL, "rt_sigaction" }, /*PPM_SC_RT_SIGPROCMASK*/ { EC_SIGNAL, "rt_sigprocmask" }, /*PPM_SC_RT_SIGPENDING*/ { EC_SIGNAL, "rt_sigpending" }, /*PPM_SC_RT_SIGTIMEDWAIT*/ { EC_SIGNAL, "rt_sigtimedwait" }, /*PPM_SC_RT_SIGQUEUEINFO*/ { EC_SIGNAL, "rt_sigqueueinfo" }, /*PPM_SC_RT_SIGSUSPEND*/ { EC_SIGNAL, "rt_sigsuspend" }, /*PPM_SC_GETCWD*/ { EC_FILE, "getcwd" }, /*PPM_SC_CAPGET*/ { EC_PROCESS, "capget" }, /* set/get capabilities of thread(s) */ /*PPM_SC_CAPSET*/ { EC_PROCESS, "capset" }, /* set/get capabilities of thread(s) */ /*PPM_SC_SENDFILE*/ { EC_FILE, "sendfile" }, /* transfer data between file descriptors */ /*PPM_SC_GETRLIMIT*/ { EC_PROCESS, "getrlimit" }, /*PPM_SC_LCHOWN*/ { EC_FILE, "lchown" }, /*PPM_SC_GETUID*/ { EC_USER, "getuid" }, /*PPM_SC_GETGID*/ { EC_USER, "getgid" }, /*PPM_SC_GETEUID*/ { EC_USER, "geteuid" }, /*PPM_SC_GETEGID*/ { EC_USER, "getegid" }, /*PPM_SC_SETREUID*/ { EC_USER, "setreuid" }, /*PPM_SC_SETREGID*/ { EC_USER, "setregid" }, /*PPM_SC_GETGROUPS*/ { EC_USER, "getgroups" }, /* returns the supplementary group IDs of the calling process */ /*PPM_SC_SETGROUPS*/ { EC_USER, "setgroups" }, /* returns the supplementary group IDs of the calling process */ /*PPM_SC_FCHOWN*/ { EC_FILE, "fchown" }, /*PPM_SC_SETRESUID*/ { EC_USER, "setresuid" }, /*PPM_SC_GETRESUID*/ { EC_USER, "getresuid" }, /*PPM_SC_SETRESGID*/ { EC_USER, "setresgid" }, /*PPM_SC_GETRESGID*/ { EC_USER, "getresgid" }, /*PPM_SC_CHOWN*/ { EC_FILE, "chown" }, /*PPM_SC_SETUID*/ { EC_USER, "setuid" }, /*PPM_SC_SETGID*/ { EC_USER, "setgid" }, /*PPM_SC_SETFSUID*/ { EC_USER, "setfsuid" }, /*PPM_SC_SETFSGID*/ { EC_USER, "setfsgid" }, /*PPM_SC_PIVOT_ROOT*/ { EC_PROCESS, "pivot_root" }, /*PPM_SC_MINCORE*/ { EC_MEMORY, "mincore" }, /* determine whether pages are resident in memory */ /*PPM_SC_MADVISE*/ { EC_MEMORY, "madvise" }, /* give advice about use of memory */ /*PPM_SC_GETTID*/ { EC_PROCESS, "gettid" }, /* returns the caller's thread ID (TID) */ /*PPM_SC_SETXATTR*/ { EC_FILE, "setxattr" }, /* set inode attribute */ /*PPM_SC_LSETXATTR*/ { EC_FILE, "lsetxattr" }, /*PPM_SC_FSETXATTR*/ { EC_FILE, "fsetxattr" }, /*PPM_SC_GETXATTR*/ { EC_FILE, "getxattr" }, /*PPM_SC_LGETXATTR*/ { EC_FILE, "lgetxattr" }, /*PPM_SC_FGETXATTR*/ { EC_FILE, "fgetxattr" }, /*PPM_SC_LISTXATTR*/ { EC_FILE, "listxattr" }, /*PPM_SC_LLISTXATTR*/ { EC_FILE, "llistxattr" }, /*PPM_SC_FLISTXATTR*/ { EC_FILE, "flistxattr" }, /*PPM_SC_REMOVEXATTR*/ { EC_FILE, "removexattr" }, /*PPM_SC_LREMOVEXATTR*/ { EC_FILE, "lremovexattr" }, /*PPM_SC_FREMOVEXATTR*/ { EC_FILE, "fremovexattr" }, /*PPM_SC_TKILL*/ { EC_SIGNAL, "tkill" }, /* send a signal to a thread */ /*PPM_SC_FUTEX*/ { EC_IPC, "futex" }, /*PPM_SC_SCHED_SETAFFINITY*/ { EC_PROCESS, "sched_setaffinity" }, /*PPM_SC_SCHED_GETAFFINITY*/ { EC_PROCESS, "sched_getaffinity" }, /*PPM_SC_SET_THREAD_AREA*/ { EC_PROCESS, "set_thread_area" }, /*PPM_SC_GET_THREAD_AREA*/ { EC_PROCESS, "get_thread_area" }, /*PPM_SC_IO_SETUP*/ { EC_IO_OTHER, "io_setup" }, /* create an asynchronous I/O context (for libaio) */ /*PPM_SC_IO_DESTROY*/ { EC_IO_OTHER, "io_destroy" }, /*PPM_SC_IO_GETEVENTS*/ { EC_IO_OTHER, "io_getevents" }, /*PPM_SC_IO_SUBMIT*/ { EC_IO_OTHER, "io_submit" }, /*PPM_SC_IO_CANCEL*/ { EC_IO_OTHER, "io_cancel" }, /*PPM_SC_EXIT_GROUP*/ { EC_IO_OTHER, "exit_group" }, /*PPM_SC_EPOLL_CREATE*/ { EC_WAIT, "epoll_create" }, /*PPM_SC_EPOLL_CTL*/ { EC_WAIT, "epoll_ctl" }, /*PPM_SC_EPOLL_WAIT*/ { EC_WAIT, "epoll_wait" }, /*PPM_SC_REMAP_FILE_PAGES*/ { EC_FILE, "remap_file_pages" }, /* create a nonlinear file mapping */ /*PPM_SC_SET_TID_ADDRESS*/ { EC_PROCESS, "set_tid_address" }, /* set pointer to thread ID */ /*PPM_SC_TIMER_CREATE*/ { EC_TIME, "timer_create" }, /*PPM_SC_TIMER_SETTIME*/ { EC_TIME, "timer_settime" }, /*PPM_SC_TIMER_GETTIME*/ { EC_TIME, "timer_gettime" }, /*PPM_SC_TIMER_GETOVERRUN*/ { EC_TIME, "timer_getoverrun" }, /*PPM_SC_TIMER_DELETE*/ { EC_TIME, "timer_delete" }, /*PPM_SC_CLOCK_SETTIME*/ { EC_TIME, "clock_settime" }, /*PPM_SC_CLOCK_GETTIME*/ { EC_TIME, "clock_gettime" }, /*PPM_SC_CLOCK_GETRES*/ { EC_TIME, "clock_getres" }, /*PPM_SC_CLOCK_NANOSLEEP*/ { EC_SLEEP, "clock_nanosleep" }, /*PPM_SC_TGKILL*/ { EC_SIGNAL, "tgkill" }, /*PPM_SC_UTIMES*/ { EC_FILE, "utimes" }, /* change file last access and modification times */ /*PPM_SC_MQ_OPEN*/ { EC_IPC, "mq_open" }, /* Message queues. See http://linux.die.net/man/7/mq_overview. */ /*PPM_SC_MQ_UNLINK*/ { EC_IPC, "mq_unlink" }, /*PPM_SC_MQ_TIMEDSEND*/ { EC_IPC, "mq_timedsend" }, /*PPM_SC_MQ_TIMEDRECEIVE*/ { EC_IPC, "mq_timedreceive" }, /*PPM_SC_MQ_NOTIFY*/ { EC_IPC, "mq_notify" }, /*PPM_SC_MQ_GETSETATTR*/ { EC_IPC, "mq_getsetattr" }, /*PPM_SC_KEXEC_LOAD*/ { EC_SYSTEM, "kexec_load" }, /* load a new kernel for later execution */ /*PPM_SC_WAITID*/ { EC_WAIT, "waitid" }, /*PPM_SC_ADD_KEY*/ { EC_SYSTEM, "add_key" }, /* add a key to the kernel's key management facility */ /*PPM_SC_REQUEST_KEY*/ { EC_SYSTEM, "request_key" }, /*PPM_SC_KEYCTL*/ { EC_SYSTEM, "keyctl" }, /*PPM_SC_IOPRIO_SET*/ { EC_PROCESS, "ioprio_set" }, /* get/set I/O scheduling class and priority */ /*PPM_SC_IOPRIO_GET*/ { EC_PROCESS, "ioprio_get" }, /* get/set I/O scheduling class and priority */ /*PPM_SC_INOTIFY_INIT*/ { EC_IPC, "inotify_init" }, /* initialize an inotify event queue instance. See http://en.wikipedia.org/wiki/Inotify. */ /*PPM_SC_INOTIFY_ADD_WATCH*/ { EC_IPC, "inotify_add_watch" }, /*PPM_SC_INOTIFY_RM_WATCH*/ { EC_IPC, "inotify_rm_watch" }, /*PPM_SC_OPENAT*/ { EC_FILE, "openat" }, /*PPM_SC_MKDIRAT*/ { EC_FILE, "mkdirat" }, /*PPM_SC_MKNODAT*/ { EC_FILE, "mknodat" }, /*PPM_SC_FCHOWNAT*/ { EC_FILE, "fchownat" }, /*PPM_SC_FUTIMESAT*/ { EC_FILE, "futimesat" }, /*PPM_SC_UNLINKAT*/ { EC_FILE, "unlinkat" }, /*PPM_SC_RENAMEAT*/ { EC_FILE, "renameat" }, /*PPM_SC_LINKAT*/ { EC_FILE, "linkat" }, /*PPM_SC_SYMLINKAT*/ { EC_FILE, "symlinkat" }, /*PPM_SC_READLINKAT*/ { EC_FILE, "readlinkat" }, /*PPM_SC_FCHMODAT*/ { EC_FILE, "fchmodat" }, /*PPM_SC_FACCESSAT*/ { EC_FILE, "faccessat" }, /*PPM_SC_PSELECT6*/ { EC_WAIT, "pselect6" }, /*PPM_SC_PPOLL*/ { EC_WAIT, "ppoll" }, /*PPM_SC_UNSHARE*/ { EC_PROCESS, "unshare" }, /* disassociate parts of the process execution context */ /*PPM_SC_SET_ROBUST_LIST*/ { EC_PROCESS, "set_robust_list" }, /* get/set list of robust futexes */ /*PPM_SC_GET_ROBUST_LIST*/ { EC_PROCESS, "get_robust_list" }, /* get/set list of robust futexes */ /*PPM_SC_SPLICE*/ { EC_IPC, "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, "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, "vmsplice" }, /* splice user pages into a pipe */ /*PPM_SC_GETCPU*/ { EC_PROCESS, "getcpu" }, /* determine CPU and NUMA node on which the calling thread is running */ /*PPM_SC_EPOLL_PWAIT*/ { EC_WAIT, "epoll_pwait" }, /*PPM_SC_UTIMENSAT*/ { EC_FILE, "utimensat" }, /* change file timestamps with nanosecond precision */ /*PPM_SC_SIGNALFD*/ { EC_SIGNAL, "signalfd" }, /* create a pollable file descriptor for accepting signals */ /*PPM_SC_TIMERFD_CREATE*/ { EC_TIME, "timerfd_create" }, /* // create and operate on a timer that delivers timer expiration notifications via a file descriptor */ /*PPM_SC_EVENTFD*/ { EC_IPC, "eventfd" }, /* create a file descriptor for event notification */ /*PPM_SC_TIMERFD_SETTIME*/ { EC_TIME, "timerfd_settime" }, /* create and operate on a timer that delivers timer expiration notifications via a file descriptor */ /*PPM_SC_TIMERFD_GETTIME*/ { EC_TIME, "timerfd_gettime" }, /* create and operate on a timer that delivers timer expiration notifications via a file descriptor */ /*PPM_SC_SIGNALFD4*/ { EC_SIGNAL, "signalfd4" }, /* create a pollable file descriptor for accepting signals */ /*PPM_SC_EVENTFD2*/ { EC_IPC, "eventfd2" }, /* create a file descriptor for event notification */ /*PPM_SC_EPOLL_CREATE1*/ { EC_WAIT, "epoll_create1" }, /* variant of epoll_create */ /*PPM_SC_DUP3*/ { EC_IO_OTHER, "dup3" }, /*PPM_SC_PIPE2*/ { EC_IPC, "pipe2" }, /*PPM_SC_INOTIFY_INIT1*/ { EC_IPC, "inotify_init1" }, /*PPM_SC_PREADV*/ { EC_IO_READ, "preadv" }, /*PPM_SC_PWRITEV*/ { EC_IO_WRITE, "pwritev" }, /*PPM_SC_RT_TGSIGQUEUEINFO*/ { EC_OTHER, "rt_tgsigqueueinfo" }, /*PPM_SC_PERF_EVENT_OPEN*/ { EC_OTHER, "perf_event_open" }, /*PPM_SC_FANOTIFY_INIT*/ { EC_IPC, "fanotify_init" }, /*PPM_SC_PRLIMIT64*/ { EC_PROCESS, "prlimit64" }, /*PPM_SC_CLOCK_ADJTIME*/ { EC_OTHER, "clock_adjtime" }, /*PPM_SC_SYNCFS*/ { EC_FILE, "syncfs" }, /*PPM_SC_SETNS*/ { EC_PROCESS, "setns" }, /* reassociate thread with a namespace */ /*PPM_SC_GETDENTS64*/ { EC_IPC, "getdents64" }, /* */ /* Non-multiplexed socket family */ /* */ /*PPM_SC_SOCKET*/ { EC_NET, "socket" }, /*PPM_SC_BIND*/ { EC_NET, "bind" }, /*PPM_SC_CONNECT*/ { EC_NET, "connect" }, /*PPM_SC_LISTEN*/ { EC_NET, "listen" }, /*PPM_SC_ACCEPT*/ { EC_NET, "accept" }, /*PPM_SC_GETSOCKNAME*/ { EC_NET, "getsockname" }, /*PPM_SC_GETPEERNAME*/ { EC_NET, "getpeername" }, /*PPM_SC_SOCKETPAIR*/ { EC_NET, "socketpair" }, /*PPM_SC_SENDTO*/ { EC_NET, "sendto" }, /*PPM_SC_RECVFROM*/ { EC_NET, "recvfrom" }, /*PPM_SC_SHUTDOWN*/ { EC_NET, "shutdown" }, /*PPM_SC_SETSOCKOPT*/ { EC_NET, "setsockopt" }, /*PPM_SC_GETSOCKOPT*/ { EC_NET, "getsockopt" }, /*PPM_SC_SENDMSG*/ { EC_NET, "sendmsg" }, /*PPM_SC_SENDMMSG*/ { EC_NET, "sendmmsg" }, /*PPM_SC_RECVMSG*/ { EC_NET, "recvmsg" }, /*PPM_SC_RECVMMSG*/ { EC_NET, "recvmmsg" }, /*PPM_SC_ACCEPT4*/ { EC_NET, "accept4" }, /* * Non-multiplexed IPC family */ /*PPM_SC_SEMOP*/ { EC_IPC, "semop" }, /*PPM_SC_SEMGET*/ { EC_IPC, "semget" }, /*PPM_SC_SEMCTL*/ { EC_IPC, "semctl" }, /*PPM_SC_MSGSND*/ { EC_IPC, "msgsnd" }, /*PPM_SC_MSGRCV*/ { EC_IPC, "msgrcv" }, /*PPM_SC_MSGGET*/ { EC_IPC, "msgget" }, /*PPM_SC_MSGCTL*/ { EC_IPC, "msgctl" }, /*PPM_SC_SHMDT*/ { EC_IPC, "shmdt" }, /*PPM_SC_SHMGET*/ { EC_IPC, "shmget" }, /*PPM_SC_SHMCTL*/ { EC_IPC, "shmctl" }, /*PPM_SC_STATFS64*/ { EC_FILE, "statfs64" }, /*PPM_SC_FSTATFS64*/ { EC_FILE, "fstatfs64" }, /*PPM_SC_FSTATAT64*/ { EC_FILE, "fstatat64" }, /*PPM_SC_SENDFILE64*/ { EC_FILE, "sendfile64" }, /*PPM_SC_UGETRLIMIT*/ { EC_PROCESS, "ugetrlimit" }, /*PPM_SC_BDFLUSH*/ { EC_OTHER, "bdflush" }, /* deprecated */ /*PPM_SC_SIGPROCMASK*/ { EC_SIGNAL, "sigprocmask" }, /* examine and change blocked signals */ /*PPM_SC_IPC*/ { EC_IPC, "ipc" }, /*PPM_SC_SOCKETCALL*/ { EC_NET, "socketcall" }, /*PPM_SC_STAT64*/ { EC_FILE, "stat64" }, /*PPM_SC_LSTAT64*/ { EC_FILE, "lstat64" }, /*PPM_SC_FSTAT64*/ { EC_FILE, "fstat64" }, /*PPM_SC_FCNTL64*/ { EC_FILE, "fcntl64" }, /*PPM_SC_MMAP2*/ { EC_FILE, "mmap2" }, /*PPM_SC__NEWSELECT*/ { EC_WAIT, "newselect" }, /*PPM_SC_SGETMASK*/ { EC_SIGNAL, "sgetmask" }, /* manipulation of signal mask (obsolete) */ /*PPM_SC_SSETMASK*/ { EC_SIGNAL, "ssetmask" }, /* manipulation of signal mask (obsolete) */ /*PPM_SC_SIGPENDING*/ { EC_SIGNAL, "sigpending" }, /* examine pending signals */ /*PPM_SC_OLDUNAME*/ { EC_SYSTEM, "olduname" }, /*PPM_SC_UMOUNT*/ { EC_FILE, "umount" }, /*PPM_SC_SIGNAL*/ { EC_SIGNAL, "signal" }, /*PPM_SC_NICE*/ { EC_PROCESS, "nice" }, /* change process priority */ /*PPM_SC_STIME*/ { EC_TIME, "stime" }, /*PPM_SC__LLSEEK*/ { EC_FILE, "llseek" }, /*PPM_SC_WAITPID*/ { EC_WAIT, "waitpid" }, /*PPM_SC_PREAD64*/ { EC_FILE, "pread64" }, /*PPM_SC_PWRITE64*/ { EC_FILE, "pwrite64" }, /*PPM_SC_ARCH_PRCTL*/ { EC_PROCESS, "arch_prctl" }, /*PPM_SC_SHMAT*/ { EC_IPC, "shmat" }, /*PPM_SC_SIGRETURN*/ { EC_SIGNAL, "sigreturn" }, /* return from signal handler and cleanup stack frame */ /*PPM_SC_FALLOCATE*/ { EC_IO_OTHER, "fallocate" }, /* manipulate file space */ /*PPM_SC_NEWFSSTAT*/ { EC_IO_OTHER, "newfstatat" }, /*PPM_SC_PROCESS_VM_READV*/ { EC_IO_OTHER, "process_vm_readv" }, /*PPM_SC_PROCESS_VM_WRITEV*/ { EC_IO_OTHER, "process_vm_writev" }, /*PPM_SC_FORK*/ { EC_IO_OTHER, "fork" }, /*PPM_SC_VFORK*/ { EC_IO_OTHER, "vfork" }, /*PPM_SC_SETUID32*/ { EC_IO_OTHER, "setuid" }, /*PPM_SC_GETUID32*/ { EC_IO_OTHER, "getuid" }, /*PPM_SC_SETGID32*/ { EC_IO_OTHER, "setgid" }, /*PPM_SC_GETEUID32*/ { EC_IO_OTHER, "geteuid" }, /*PPM_SC_GETGID32*/ { EC_IO_OTHER, "getgid" }, /*PPM_SC_SETRESUID32*/ { EC_IO_OTHER, "setresuid" }, /*PPM_SC_SETRESGID32*/ { EC_IO_OTHER, "setresgid" }, /*PPM_SC_GETRESUID32*/ { EC_IO_OTHER, "getresuid" }, /*PPM_SC_GETRESGID32*/ { EC_IO_OTHER, "getresgid" }, }; bool validate_info_table_size() { return (sizeof(g_syscall_info_table) / sizeof(g_syscall_info_table[0]) == PPM_SC_MAX); }sysdig-0.8.0/userspace/libscap/uthash.h000066400000000000000000001643651265472057500201310ustar00rootroot00000000000000/* 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.8.0/userspace/libsinsp/000077500000000000000000000000001265472057500166535ustar00rootroot00000000000000sysdig-0.8.0/userspace/libsinsp/.gitignore000066400000000000000000000000121265472057500206340ustar00rootroot00000000000000unit_testssysdig-0.8.0/userspace/libsinsp/CMakeLists.txt000066400000000000000000000027461265472057500214240ustar00rootroot00000000000000include_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}") 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 dumper.cpp fdinfo.cpp filter.cpp filterchecks.cpp ifinfo.cpp k8s.cpp k8s_collector.cpp k8s_component.cpp k8s_state.cpp k8s_dispatcher.cpp k8s_event_data.cpp k8s_http.cpp k8s_net.cpp marathon_component.cpp marathon_dispatcher.cpp marathon_http.cpp memmem.cpp mesos.cpp mesos_collector.cpp mesos_component.cpp mesos_event_data.cpp mesos_http.cpp mesos_state.cpp internal_metrics.cpp "${JSONCPP_LIB_SRC}" logger.cpp parsers.cpp protodecoder.cpp threadinfo.cpp sinsp.cpp stats.cpp table.cpp uri.cpp utils.cpp viewinfo.cpp) target_link_libraries(sinsp scap "${JSONCPP_LIB}") if(NOT WIN32) if(USE_BUNDLED_LUAJIT) add_dependencies(sinsp luajit) endif() if(NOT APPLE) target_link_libraries(sinsp "${B64_LIB}" "${CURL_LIBRARIES}" "${OPENSSL_LIBRARY_SSL}" "${OPENSSL_LIBRARY_CRYPTO}") endif() target_link_libraries(sinsp "${LUAJIT_LIB}" dl pthread) else() target_link_libraries(sinsp "${LUAJIT_LIB}") endif() sysdig-0.8.0/userspace/libsinsp/chisel.cpp000066400000000000000000001002541265472057500206300ustar00rootroot00000000000000/* 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 #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_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}, {"make_ts", &lua_cbacks::make_ts}, {"run_sysdig", &lua_cbacks::run_sysdig}, {"end_capture", &lua_cbacks::end_capture}, {"log", &lua_cbacks::log}, #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}, {"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; #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) { if(m_filter) { delete m_filter; m_filter = NULL; } if(filterstr != "") { m_filter = new sinsp_filter(m_inspector, filterstr); } } 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; } #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; 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(); #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; 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 == "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 == "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); } cols->push_back(sinsp_view_column_info(field, name, description, colsize, (uint32_t)flags, aggregation, groupby_aggregation, tags)); } 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; bool is_root = false; 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 == "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"); } } 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); 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 as is // if(!openfile(m_filename, &is)) { // // Try to add the .sc extension // if(!openfile(m_filename + ".sc", &is)) { if(!openfile(m_filename + ".lua", &is)) { throw sinsp_exception("can't open file " + m_filename); } } } // // Bring the file into a string // string docstr((istreambuf_iterator(is)), istreambuf_iterator()); #ifdef HAS_LUA_CHISELS // // Rewind the stream // is.seekg(0); // // 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; } 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 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); } // // 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) { 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; ASSERT(delta > 0); } 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; } } } 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.8.0/userspace/libsinsp/chisel.h000066400000000000000000000075601265472057500203030ustar00rootroot00000000000000/* 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); ~chiselinfo(); sinsp_filter* m_filter; sinsp_evt_formatter* m_formatter; sinsp_dumper* m_dumper; uint64_t m_callback_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[1024]; chiselinfo* m_lua_cinfo; string m_new_chisel_to_exec; friend class lua_cbacks; }; /*@}*/ #endif // HAS_CHISELS sysdig-0.8.0/userspace/libsinsp/chisel_api.cpp000066400000000000000000000667161265472057500214770ustar00rootroot00000000000000/* 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 #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: 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: lua_pushstring(ls, (char*)rawval); return 1; case PT_BYTEBUF: if(rawval[len] == 0) { lua_pushstring(ls, (char*)rawval); 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_pushstring(ls, (char*)ch->m_lua_fld_storage); 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); 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 { 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::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(lua_State *ls) { threadinfo_map_iterator_t it; unordered_map::iterator fdit; uint32_t j; 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); try { filter = new sinsp_filter(ch->m_inspector, filterstr, true); } 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 = 0; 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); 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"); vector* env = &(it->second.m_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); 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); 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_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 { 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::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::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; } #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.8.0/userspace/libsinsp/chisel_api.h000066400000000000000000000040411265472057500211230ustar00rootroot00000000000000/* 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 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_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 set_event_formatter(lua_State *ls); static int set_interval_ns(lua_State *ls); static int set_interval_s(lua_State *ls); static int exec(lua_State *ls); static int log(lua_State *ls); #ifdef HAS_ANALYZER static int push_metric(lua_State *ls); #endif }; #endif // HAS_CHISELS sysdig-0.8.0/userspace/libsinsp/container.cpp000066400000000000000000000447311265472057500213520ustar00rootroot00000000000000/* 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" 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()) { 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; } bool sinsp_container_manager::set_mesos_task_id(sinsp_container_info* container, const string& task_id, int64_t ptid) { ASSERT(container); const string& container_id = container->m_id; if(task_id.empty() && -1 == ptid) { g_logger.log("Mesos container [" + container_id + "] detection attempted with insufficient information provided.", sinsp_logger::SEV_WARNING); return false; } // 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 env variable, // so we peek into the parent process environment to discover it if(container) { if(container->m_mesos_task_id.empty()) { if(!task_id.empty()) { container->m_mesos_task_id = task_id; g_logger.log("Mesos container: [" + container_id + "], Mesos task ID: [" + task_id + ']', sinsp_logger::SEV_DEBUG); return true; } else if(-1 != ptid && container->m_type == CT_MESOS) { sinsp_threadinfo* tinfo = m_inspector->find_thread(ptid, true); if(tinfo) { string mtid = tinfo->get_env("MESOS_TASK_ID"); if(!mtid.empty()) { g_logger.log("Mesos native container: [" + container_id + "], Mesos task ID: " + mtid + ", PTID:[" + std::to_string(ptid) +']', sinsp_logger::SEV_DEBUG); container->m_mesos_task_id = mtid; return true; } } } } } 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; 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) { container_info.m_type = CT_LXC; container_info.m_id = cgroup.substr(pos + sizeof("/lxc/") - 1); 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); valid_id = true; string mesos_task_id = tinfo->get_env("MESOS_TASK_ID"); int64_t ptid = tinfo->m_ptid; if(!mesos_task_id.empty() || (-1 != ptid)) { set_mesos_task_id(&container_info, mesos_task_id, ptid); } break; } } string rkt_podid, rkt_appname; if(!valid_id) { // Try parsing from process root, // Strings used to detect rkt stage1-cores pods 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 != string::npos) { 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* tinfo_it = tinfo; while(!valid_id && tinfo_it != nullptr) { for(const auto& env_var : tinfo_it->m_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; break; } } tinfo_it = tinfo_it->get_parent_thread(); } } } 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 != string::npos) { 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 = 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); } m_containers.insert(std::make_pair(container_info.m_id, container_info)); if(container_to_sinsp_event(container_info, &m_inspector->m_meta_evt, SP_EVT_BUF_SIZE)) { m_inspector->m_meta_evt_pending = true; } } } return valid_id; } bool sinsp_container_manager::container_to_sinsp_event(const sinsp_container_info& container_info, sinsp_evt* evt, size_t evt_len) { size_t totlen = sizeof(scap_evt) + 4 * sizeof(uint16_t) + container_info.m_id.length() + 1 + sizeof(uint32_t) + container_info.m_name.length() + 1 + container_info.m_image.length() + 1; if(totlen > evt_len) { ASSERT(false); return false; } evt->m_cpuid = 0; evt->m_evtnum = 0; 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_E; uint16_t* lens = (uint16_t*)((char *)scapevt + sizeof(struct ppm_evt_hdr)); char* valptr = (char*)lens + 4 * sizeof(uint16_t); lens[0] = (uint16_t)container_info.m_id.length() + 1; memcpy(valptr, container_info.m_id.c_str(), lens[0]); valptr += lens[0]; lens[1] = sizeof(uint32_t); memcpy(valptr, &container_info.m_type, lens[1]); valptr += lens[1]; lens[2] = (uint16_t)container_info.m_name.length() + 1; memcpy(valptr, container_info.m_name.c_str(), lens[2]); valptr += lens[2]; lens[3] = (uint16_t)container_info.m_image.length() + 1; memcpy(valptr, container_info.m_image.c_str(), lens[3]); valptr += lens[3]; 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))) != 0) { if(res == -1) { 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(); 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(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"]; static const string mti = "MESOS_TASK_ID"; string mesos_task_id; for(const auto& env_var : env_vars) { if(env_var.isString()) { mesos_task_id = env_var.asString(); if((mesos_task_id.length() > (mti.length() + 1)) && (mesos_task_id.substr(0, mti.length()) == mti)) { container->m_mesos_task_id = mesos_task_id.substr(mti.length() + 1); g_logger.log("Mesos Docker container: [" + root["Id"].asString() + "], Mesos task ID: [" + container->m_mesos_task_id + ']', sinsp_logger::SEV_INFO); break; } } } return true; } 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; unordered_map image_ports; 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; } for(const auto& image_port : jroot["app"]["ports"]) { image_ports.emplace(image_port["name"].asString(), image_port["port"].asUInt()); } 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) { 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); if(reader.parse(pod_manifest, jroot) && jroot.size() > 0) { for(const auto& jport : jroot["ports"]) { auto host_port = jport["hostPort"].asUInt(); if(host_port > 0) { sinsp_container_info::container_port_mapping port_mapping; port_mapping.m_host_port = host_port; port_mapping.m_container_port = image_ports.at(jport["name"].asString()); 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; } 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(it->second, &m_inspector->m_meta_evt, SP_EVT_BUF_SIZE)) { 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.8.0/userspace/libsinsp/container.h000066400000000000000000000044671265472057500210210ustar00rootroot00000000000000/* 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; }; sinsp_container_info(): m_container_ip(0) { } string m_id; sinsp_container_type m_type; string m_name; string m_image; uint32_t m_container_ip; vector m_port_mappings; map m_labels; string m_mesos_task_id; }; 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); bool set_mesos_task_id(sinsp_container_info* container, const string& task_id, int64_t ptid = -1); string get_mesos_task_id(const string& container_id); private: bool container_to_sinsp_event(const sinsp_container_info& container_info, sinsp_evt* evt, size_t evt_len); bool parse_docker(sinsp_container_info* container); 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.8.0/userspace/libsinsp/ctext.cpp000066400000000000000000000744751265472057500205270ustar00rootroot00000000000000/* 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; string query = to_search_in->_query; if(!to_search_in) { return -1; } 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.8.0/userspace/libsinsp/ctext.h000066400000000000000000000343361265472057500201640ustar00rootroot00000000000000/* 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.8.0/userspace/libsinsp/cursescomponents.cpp000066400000000000000000001223661265472057500230030ustar00rootroot00000000000000/* 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 #ifndef NOCURSESUI #include #include "table.h" #include "ctext.h" #include "cursescomponents.h" #include "cursestable.h" #include "cursesui.h" #include "utils.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; } 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, bool is_spectro_drilldown) { ASSERT(inspector != NULL); ASSERT(parent != NULL); m_parent = parent; m_win = NULL; m_ctext = NULL; m_filter = NULL; m_inspector = inspector; n_prints = 0; m_paused = false; m_sidemenu = NULL; m_viz_type = viz_type; 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; // // visualization-type inits // if(m_viz_type == VIEW_ID_DIG) { if(is_spectro_drilldown) { if(m_parent->m_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(m_parent->m_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); } } config.m_do_wrap = false; } else { m_formatter = NULL; 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_formatter) { delete m_formatter; } // // 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) { m_filter = new sinsp_filter(m_inspector, filter); } 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) { // // Drop any non I/O event // ppm_event_flags eflags = evt->get_flags(); if(!(eflags & EF_READS_FROM_FD || eflags & EF_WRITES_TO_FD)) { return; } // // Get and validate the length // sinsp_evt_param* parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); int64_t len = *(int64_t*)parinfo->m_val; if(len <= 0) { return; } // // Get thread and fd // sinsp_threadinfo* m_tinfo = evt->get_thread_info(); if(m_tinfo == NULL) { return; } sinsp_fdinfo_t* m_fdinfo = evt->get_fd_info(); if(m_fdinfo == NULL) { return; } string fdname = m_fdinfo->m_name; if(fdname == "") { fdname = "unnamed FD"; } // // Get the buffer // const char* resolved_argstr; const char* argstr; argstr = evt->get_param_value_str("data", &resolved_argstr, m_inspector->get_buffer_format()); if(argstr != NULL) { // // Create the info string // string info_str = "------ "; string dirstr; string cnstr; 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 + fdname + " (" + m_tinfo->m_comm.c_str() + ")"; // // Sanitize the info string // info_str.erase(remove_if(info_str.begin(), info_str.end(), g_invalidchar()), info_str.end()); // // 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_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_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->m_filter != "") { 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->m_filter.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 to 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" "You can drill down multiple times, by keeping 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 selction 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 the columns with 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.8.0/userspace/libsinsp/cursescomponents.h000066400000000000000000000116741265472057500224470ustar00rootroot00000000000000/* 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 #ifndef NOCURSESUI #define TABLE_WIDTH 10000 #define TABLE_Y_START 2 #include 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 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, bool is_spectro_drilldown); ~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; }; 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.8.0/userspace/libsinsp/cursesspectro.cpp000066400000000000000000000317611265472057500222730ustar00rootroot00000000000000/* 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 #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) { 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; // // 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) { m_selstart_x = -1; m_selstart_y = -1; 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_last_mevent.bstate & BUTTON1_RELEASED) { 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; } 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 (evt.latency>=" + to_string(start_latency) + " and evt.latency<" + 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(); return STA_DIG; } else { 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.8.0/userspace/libsinsp/cursesspectro.h000066400000000000000000000057501265472057500217370ustar00rootroot00000000000000/* 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); ~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; friend class curses_spectro_sidemenu; }; #endif // NOCURSESUI #endif // CSYSDIGsysdig-0.8.0/userspace/libsinsp/cursestable.cpp000066400000000000000000000416601265472057500217020ustar00rootroot00000000000000/* 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_wait() { string wstr; if(m_inspector->is_live()) { wstr = "Collecting Data"; } else { if(m_parent->is_eof()) { wstr = "No Data For This View"; } } wattrset(m_tblwin, m_parent->m_colors[sinsp_cursesui::PROCESS]); mvwprintw(m_tblwin, m_parent->m_screenh / 2, m_parent->m_screenw / 2 - wstr.size() / 2, wstr.c_str()); } 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.8.0/userspace/libsinsp/cursestable.h000066400000000000000000000045071265472057500213460ustar00rootroot00000000000000/* 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(); 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.8.0/userspace/libsinsp/cursesui.cpp000066400000000000000000001522351265472057500212310ustar00rootroot00000000000000/* 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 "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; #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 /////////////////////////////////////////////////////////////////////////////// // 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, bool raw_output, bool is_mousedrag_available) { 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_raw_output = raw_output; m_truncated_input = false; #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(!m_raw_output) { // // 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_BLUE,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; } #ifndef NOCURSESUI if(!m_raw_output) { 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) { bool is_spectro_dig = false; // // 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; } #ifndef NOCURSESUI if(!m_raw_output) { if(m_viz != NULL) { delete m_viz; m_viz = NULL; } if(m_spectro != NULL) { delete m_spectro; m_spectro = NULL; is_spectro_dig = true; } 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(); // // 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_raw_output); } 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_raw_output); } 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_raw_output); } else { m_datatable = new sinsp_table(m_inspector, ty, m_refresh_interval_ns, m_raw_output); } } else { ASSERT(false); } try { m_datatable->configure(&wi->m_columns, m_complete_filter, wi->m_use_defaults); } catch(...) { delete m_datatable; m_datatable = NULL; throw; } m_datatable->set_sorting_col(wi->m_sortingcol); } #ifndef NOCURSESUI else { if(m_raw_output) { return; } // // Create the visualization component // m_spy_box = new curses_textbox(m_inspector, this, m_selected_view, is_spectro_dig); m_spy_box->reset(); m_chart = m_spy_box; m_spy_box->set_filter(m_complete_filter); } if(m_raw_output) { return; } // // If we need a table or spectrogram visualization, allocate it and set it up // 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_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_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 "; } } } 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; // // 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); } #ifndef NOCURSESUI void sinsp_cursesui::populate_view_sidemenu(string field, vector* viewlist) { uint32_t k = 0; viewlist->clear(); 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; } void sinsp_cursesui::handle_end_of_sample(sinsp_evt* evt, int32_t next_res) { m_datatable->flush(evt); // // It's time to refresh the data for this chart. // First of all, create the data for the chart // vector* sample = m_datatable->get_sample(get_time_delta()); #ifndef NOCURSESUI if(!m_raw_output) { // // 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_raw_output) { 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() { if(m_is_filter_sysdig) { m_complete_filter = "(" + m_complete_filter + ") and (" + m_manual_filter + ")"; } else { m_complete_filter = m_cmdline_capture_filter; m_complete_filter = combine_filters(m_complete_filter, m_sel_hierarchy.tofilter()); // // 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)->m_filter); } } } void sinsp_cursesui::switch_view(bool is_spy_switch) { string field; if(m_sel_hierarchy.size() > 0) { sinsp_ui_selection_info* psinfo = m_sel_hierarchy.at(m_sel_hierarchy.size() - 1); field = psinfo->m_field; } #ifndef NOCURSESUI if(!m_raw_output) { // // 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 // // 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_raw_output) { 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, 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; // loris 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()); m_sel_hierarchy.push_back(field, val, m_views.at(m_selected_view)->m_filter, m_selected_view, m_selected_view_sidemenu_entry, &rowkeybak, srtcol, m_manual_filter, m_is_filter_sysdig, m_datatable->is_sorting_ascending()); 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, uint32_t new_view_num, filtercheck_field_info* info) { // // unpause the thing if it's paused // if(m_paused) { pause(); } // // escape string parameters // if(info->m_type & PT_CHARBUF) { string escape = "\""; val = escape + val + escape; } // // Do the drilldown // #ifndef NOCURSESUI sinsp_table_field* rowkey = m_datatable->get_row_key(m_viz->m_selct); #else sinsp_table_field* rowkey = NULL; #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(); m_sel_hierarchy.push_back(field, val, m_views.at(m_selected_view)->m_filter, m_selected_view, m_selected_view_sidemenu_entry, &rowkeybak, srtcol, m_manual_filter, m_is_filter_sysdig, m_datatable->is_sorting_ascending()); m_selected_view = new_view_num; // // Reset the filter // m_manual_filter = ""; 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(); // m_selected_sidemenu_entry = 0; 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, filtercheck_field_info* info) { 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, j, info); } } 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, j, info); } } } return false; } bool sinsp_cursesui::spectro_selection(string field, string val, filtercheck_field_info* info, sysdig_table_action ta) { uint32_t j = 0; string spectro_name; 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, j, info); } } 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* sinfo = m_sel_hierarchy.at(m_sel_hierarchy.size() - 1); m_manual_filter = ""; if(m_sel_hierarchy.size() > 1) { sinsp_ui_selection_info* 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_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(); 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* f; try { f = new sinsp_filter(m_inspector, *str); } 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->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 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 #endif // CSYSDIG sysdig-0.8.0/userspace/libsinsp/cursesui.h000066400000000000000000000343361265472057500206770ustar00rootroot00000000000000/* 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 VIEW_ID_SPY -1 #define VIEW_ID_DIG -2 #define VIEW_ID_INFO -3 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, 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) { m_field = field; 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_rowkey = *rowkey; } string m_field; string m_val; 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; }; class sinsp_ui_selection_hierarchy { public: void push_back(string field, string val, 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) { m_hierarchy.push_back(sinsp_ui_selection_info(field, val, view_filter, prev_selected_view, prev_selected_sidemenu_entry, rowkey, prev_sorting_col, prev_manual_filter, prev_is_filter_sysdig, prev_is_sorting_ascending)); } ~sinsp_ui_selection_hierarchy() { for(auto e : m_hierarchy) { if(e.m_rowkey.m_val != NULL) { delete [] e.m_rowkey.m_val; } } } string tofilter() { string res; uint32_t j; uint32_t hs = (uint32_t)m_hierarchy.size(); for(j = 0; j < hs; j++) { bool has_filter = false; if(m_hierarchy[j].m_view_filter != "") { has_filter = true; } if(has_filter) { if(hs > 1) { res += "("; } res += "("; res += m_hierarchy[j].m_view_filter; res += ") and "; } res += m_hierarchy[j].m_field; res += "="; res += m_hierarchy[j].m_val; if(has_filter && hs > 1) { res += ")"; } if(j < m_hierarchy.size() - 1) { res += " and "; } } 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 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, bool raw_output, bool is_mousedrag_available); ~sinsp_cursesui(); void configure(sinsp_view_manager* views); void start(bool is_drilldown, bool is_spy_switch); sinsp_view_info* get_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; } #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, bool is_dig); sysdig_table_action handle_input(int ch); // // 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 // #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); // // Some events require that we perform additional actions // switch(ta) { case STA_QUIT: return true; case STA_SWITCH_VIEW: switch_view(false); return false; case STA_SWITCH_SPY: switch_view(true); return false; case STA_DRILLDOWN: { auto res = m_datatable->get_row_key_name_and_val(m_viz->m_selct); if(res.first != NULL) { drilldown(res.first->m_name, res.second.c_str(), res.first); } } return false; case STA_DRILLUP: drillup(); return false; case STA_SPECTRO: case STA_SPECTRO_FILE: { auto res = m_datatable->get_row_key_name_and_val(m_viz->m_selct); if(res.first != NULL) { spectro_selection(res.first->m_name, res.second.c_str(), res.first, ta); } } return false; case STA_SPY: { auto res = m_datatable->get_row_key_name_and_val(m_viz->m_selct); if(res.first != NULL) { spy_selection(res.first->m_name, res.second.c_str(), false); } } return false; case STA_DIG: { if(m_viz) { auto res = m_datatable->get_row_key_name_and_val(m_viz->m_selct); if(res.first != NULL) { spy_selection(res.first->m_name, res.second.c_str(), true); } } else { ASSERT(m_spectro); spy_selection("", "", true); } } return false; case STA_NONE: break; default: ASSERT(false); break; } } if(ninputs == 0) { m_last_input_check_ts = ts; } } #endif // // 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_raw_output) { return true; } else { return false; } } // // Perform event processing // #ifndef NOCURSESUI if(m_spy_box) { m_spy_box->process_event(evt, next_res); } else #endif { 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; bool m_is_mousedrag_available; private: 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, filtercheck_field_info* info, sysdig_table_action ta); bool do_drilldown(string field, string val, uint32_t new_view_num, filtercheck_field_info* info); // returns false if there is no suitable drill down view for this field bool drilldown(string field, string val, filtercheck_field_info* info); // returns false if we are already at the top of the hierarchy bool drillup(); void create_complete_filter(); #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_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; bool m_raw_output; bool m_truncated_input; }; #endif // CSYSDIG sysdig-0.8.0/userspace/libsinsp/cyclewriter.cpp000066400000000000000000000120021265472057500217060ustar00rootroot00000000000000#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_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.8.0/userspace/libsinsp/cyclewriter.h000066400000000000000000000066031265472057500213650ustar00rootroot00000000000000#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.8.0/userspace/libsinsp/doxygen/000077500000000000000000000000001265472057500203305ustar00rootroot00000000000000sysdig-0.8.0/userspace/libsinsp/doxygen/conf.dox000066400000000000000000000264561265472057500220060ustar00rootroot00000000000000# 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.8.0/userspace/libsinsp/doxygen/footer.html000066400000000000000000000000301265472057500225050ustar00rootroot00000000000000
sysdig-0.8.0/userspace/libsinsp/doxygen/header.html000066400000000000000000000024221265472057500224460ustar00rootroot00000000000000--- layout: default title: sysdig | libsinsp ---

$projectname $projectnumber

(Generated by Doxygen)

$projectbrief

$projectbrief
$searchbox
sysdig-0.8.0/userspace/libsinsp/dumper.cpp000066400000000000000000000041501265472057500206530ustar00rootroot00000000000000/* 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; } sinsp_dumper::~sinsp_dumper() { if(m_dumper != NULL) { scap_dump_close(m_dumper); } } void sinsp_dumper::open(const string& filename, bool compress) { 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(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)); } m_inspector->m_container_manager.dump_containers(m_dumper); } void sinsp_dumper::dump(sinsp_evt* evt) { if(m_dumper == NULL) { throw sinsp_exception("dumper not opened yet"); } int32_t res = scap_dump(m_inspector->m_h, m_dumper, evt->m_pevt, evt->m_cpuid, 0); if(res != SCAP_SUCCESS) { throw sinsp_exception(scap_getlasterr(m_inspector->m_h)); } } uint64_t sinsp_dumper::written_bytes() { if(m_dumper == NULL) { throw sinsp_exception("dumper not opened yet"); } int64_t written_bytes = scap_dump_get_offset(m_dumper); if(written_bytes == -1) { throw sinsp_exception("error getting offset"); } return written_bytes; } void sinsp_dumper::flush() { if(m_dumper == NULL) { throw sinsp_exception("dumper not opened yet"); } scap_dump_flush(m_dumper); } sysdig-0.8.0/userspace/libsinsp/dumper.h000066400000000000000000000033501265472057500203210ustar00rootroot00000000000000/* 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); ~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. \note There's no close() because the file is closed when the dumper is destroyed. */ void open(const string& filename, bool compress); /*! \brief Return the current size of a tracefile. \return The current size of the dump file. */ uint64_t written_bytes(); /*! \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); private: sinsp* m_inspector; scap_dumper_t* m_dumper; }; /*@}*/ sysdig-0.8.0/userspace/libsinsp/event.cpp000066400000000000000000001337531265472057500205140ustar00rootroot00000000000000/* 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, ustr, xstr) do { \ 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_params_loaded = false; 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_params_loaded = false; m_tinfo = NULL; #ifdef _DEBUG m_filtered_out = false; #endif m_event_info_table = g_infotables.m_event_info; } sinsp_evt::~sinsp_evt() { } uint64_t sinsp_evt::get_ts() { return m_pevt->ts; } 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); } sinsp_fdinfo_t* sinsp_evt::get_fd_info() { return m_fdinfo; } 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_params_loaded) { load_params(); m_params_loaded = true; } return (uint32_t)m_params.size(); } sinsp_evt_param *sinsp_evt::get_param(uint32_t id) { if(!m_params_loaded) { load_params(); m_params_loaded = true; } return &(m_params[id]); } const char *sinsp_evt::get_param_name(uint32_t id) { if(!m_params_loaded) { load_params(); m_params_loaded = true; } 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_params_loaded) { load_params(); m_params_loaded = true; } 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; 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) { 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; sanitized_str.erase(remove_if(sanitized_str.begin(), sanitized_str.end(), g_invalidchar()), sanitized_str.end()); (*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; sanitized_str.erase(remove_if(sanitized_str.begin(), sanitized_str.end(), g_invalidchar()), sanitized_str.end()); // // 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_params_loaded) { load_params(); m_params_loaded = true; } // // 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::Value::nullRef; break; } else if(payload[0] == AF_UNIX) { ASSERT(payload_len > 1); // // Sanitize the file string. // string sanitized_str = payload + 1; sanitized_str.erase(remove_if(sanitized_str.begin(), sanitized_str.end(), g_invalidchar()), sanitized_str.end()); 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::Value::nullRef; 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, sip6, 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; sanitized_str.erase(remove_if(sanitized_str.begin(), sanitized_str.end(), g_invalidchar()), sanitized_str.end()); 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_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_params_loaded) { load_params(); m_params_loaded = true; } // // 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, 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, 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, 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, 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, PRIu8, 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, PRIu16, 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, PRIu32, 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, PRIu64, 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) { 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; sanitized_str.erase(remove_if(sanitized_str.begin(), sanitized_str.end(), g_invalidchar()), sanitized_str.end()); 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, sip6, 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; sanitized_str.erase(remove_if(sanitized_str.begin(), sanitized_str.end(), g_invalidchar()), sanitized_str.end()); 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_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_params_loaded) { load_params(); m_params_loaded = true; } // // 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_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: 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) { scap_dump_flags dflags = SCAP_DF_NONE; *should_drop = false; if(m_filtered_out) { if(m_inspector->m_isfatfile_enabled) { ppm_event_flags eflags = get_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; } } } return dflags; } #endif sysdig-0.8.0/userspace/libsinsp/event.h000066400000000000000000000245641265472057500201600ustar00rootroot00000000000000/* 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, ///< this field can only be used as a filter. EPF_PRINT_ONLY, ///< this field can only be printed. EPF_REQUIRES_ARGUMENT, ///< this field includes an argument, under the form 'property.argument'. EPF_TABLE_ONLY, ///< 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 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_BASE64 = (1 << 6), ///< Base64 output PF_JSONEOLS = (1 << 7), ///< Json formatting with data in hexadecimal format PF_JSONHEX = (1 << 8), ///< Json formatting with data in hexadecimal format PF_JSONHEXASCII = (1 << 9), ///< Json formatting with data in hexadecimal + ASCII format PF_JSONBASE64 = (1 << 10), ///< 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_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 */ uint64_t get_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. */ sinsp_fdinfo_t* get_fd_info(); /*! \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); #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_params_loaded = false; m_info = &(m_event_info_table[m_pevt->type]); m_tinfo = NULL; m_fdinfo = NULL; m_iosize = 0; } inline void init(uint8_t* evdata, uint16_t cpuid) { m_params_loaded = false; 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; } 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 sinsp* m_inspector; scap_evt* m_pevt; uint16_t m_cpuid; uint64_t m_evtnum; 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_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; }; /*@}*/ sysdig-0.8.0/userspace/libsinsp/eventformatter.cpp000066400000000000000000000146761265472057500224420ustar00rootroot00000000000000/* 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; m_first = true; 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); 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) { m_tokens.push_back(new rawstring_check(lfmt.substr(last_nontoken_str_start, j - last_nontoken_str_start))); m_tokenlens.push_back(0); } } bool sinsp_evt_formatter::on_capture_end(OUT string* res) { res->clear(); if(!m_first && (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) = ']'; } 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::Value::nullRef && m_require_all_values) { retval = false; continue; } fi = m_tokens[j]->get_field_info(); if(fi && fi->m_name) { 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) { if(m_first) { // Give it the opening stanza of a JSON array (*res) = '['; m_first = false; } else { // Otherwise say this is another object in an // existing JSON array (*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 sysdig-0.8.0/userspace/libsinsp/eventformatter.h000066400000000000000000000043561265472057500221010ustar00rootroot00000000000000/* 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; // Is this the first to_string call? bool m_first; Json::Value m_root; Json::FastWriter m_writer; }; /*@}*/ sysdig-0.8.0/userspace/libsinsp/fdinfo.cpp000066400000000000000000000217411265472057500206310ustar00rootroot00000000000000/* 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; 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: 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; default: // ASSERT(false); return '?'; } } template<> char* sinsp_fdinfo_t::get_typestring() { switch(m_type) { 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"; default: return (char*)""; } } template<> string sinsp_fdinfo_t::tostring_clean() { string m_tstr = m_name; m_tstr.erase(remove_if(m_tstr.begin(), m_tstr.end(), g_invalidchar()), m_tstr.end()); 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.8.0/userspace/libsinsp/fdinfo.h000066400000000000000000000227541265472057500203030ustar00rootroot00000000000000/* 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' /** @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; } /*! \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_IPV4_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), // Pipe-specific flags FLAGS_IS_SOCKET_PIPE = (1 << 6), }; 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(); } T* m_usrstate; uint32_t m_flags; uint64_t m_ino; fd_callbacks_info* m_callbaks; 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; }; /*@}*/ /////////////////////////////////////////////////////////////////////////////// // 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.8.0/userspace/libsinsp/filter.cpp000066400000000000000000001124711265472057500206520ustar00rootroot00000000000000/* 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. // #include "sinsp.h" #include "sinsp_int.h" #ifdef HAS_FILTERING #include "filter.h" #include "filterchecks.h" #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()); add_filter_check(new sinsp_filter_check_k8s()); } 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); 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(ppm_cmp_operator 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); default: throw sinsp_exception("'contains' not supported for numeric filters"); return false; } } bool flt_compare_int64(ppm_cmp_operator 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_IN: throw sinsp_exception("'in' not supported for numeric filters"); return false; default: throw sinsp_exception("'unknown' not supported for numeric filters"); return false; } } bool flt_compare_string(ppm_cmp_operator 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_IN: return (strstr(operand1, operand2) != NULL); 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(ppm_cmp_operator 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_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(ppm_cmp_operator 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); default: throw sinsp_exception("'contains' not supported for numeric filters"); return false; } } bool flt_compare(ppm_cmp_operator 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_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(ppm_cmp_operator 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_val_storage(256) { 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; } 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_HEX) { return rawval_to_string(rawval, finfo, len); } else { ASSERT(false); return Json::Value::nullRef; } 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_HEX) { return rawval_to_string(rawval, finfo, len); } else { ASSERT(false); return Json::Value::nullRef; } 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_HEX) { return rawval_to_string(rawval, finfo, len); } else { ASSERT(false); return Json::Value::nullRef; } 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_HEX) { return rawval_to_string(rawval, finfo, len); } else { ASSERT(false); return Json::Value::nullRef; } 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_HEX) { return rawval_to_string(rawval, finfo, len); } else { ASSERT(false); return Json::Value::nullRef; } 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_HEX) { return rawval_to_string(rawval, finfo, len); } else { ASSERT(false); return Json::Value::nullRef; } 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_HEX) { return rawval_to_string(rawval, finfo, len); } else { ASSERT(false); return Json::Value::nullRef; } case PT_SOCKADDR: case PT_SOCKFAMILY: ASSERT(false); return Json::Value::nullRef; case PT_BOOL: return Json::Value((bool)(*(uint32_t*)rawval != 0)); case PT_CHARBUF: 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_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_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_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_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_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_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_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_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: return (char*)rawval; case PT_BYTEBUF: if(rawval[len] == 0) { return (char*)rawval; } else { ASSERT(len < 1024 * 1024); if(len >= m_val_storage.size()) { m_val_storage.resize(len + 1); } memcpy(&m_val_storage[0], rawval, len); m_val_storage[len] = 0; return (char*)&m_val_storage[0]; } 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)); } } void sinsp_filter_check::string_to_rawval(const char* str, uint32_t len, ppm_param_type ptype) { switch(ptype) { case PT_INT8: *(int8_t*)(&m_val_storage[0]) = sinsp_numparser::parsed8(str); break; case PT_INT16: *(int16_t*)(&m_val_storage[0]) = sinsp_numparser::parsed16(str); break; case PT_INT32: *(int32_t*)(&m_val_storage[0]) = sinsp_numparser::parsed32(str); break; case PT_INT64: case PT_FD: case PT_ERRNO: *(int64_t*)(&m_val_storage[0]) = sinsp_numparser::parsed64(str); break; case PT_L4PROTO: // This can be resolved in the future case PT_FLAGS8: case PT_UINT8: *(uint8_t*)(&m_val_storage[0]) = sinsp_numparser::parseu8(str); break; case PT_PORT: { string in(str); if(in.empty()) { *(uint16_t*)(&m_val_storage[0]) = 0; } else { // if the string is made only of numbers if(strspn(in.c_str(), "0123456789") == in.size()) { *(uint16_t*)(&m_val_storage[0]) = stoi(in); } else { *(uint16_t*)(&m_val_storage[0]) = ntohs(getservbyname(in.c_str(), NULL)->s_port); } } break; } case PT_FLAGS16: case PT_UINT16: *(uint16_t*)(&m_val_storage[0]) = sinsp_numparser::parseu16(str); break; case PT_FLAGS32: case PT_UINT32: *(uint32_t*)(&m_val_storage[0]) = sinsp_numparser::parseu32(str); break; case PT_UINT64: *(uint64_t*)(&m_val_storage[0]) = sinsp_numparser::parseu64(str); break; case PT_RELTIME: case PT_ABSTIME: *(uint64_t*)(&m_val_storage[0]) = sinsp_numparser::parseu64(str); break; case PT_CHARBUF: case PT_SOCKADDR: case PT_SOCKFAMILY: { len = (uint32_t)strlen(str); if(len >= m_val_storage.size()) { throw sinsp_exception("filter parameter too long:" + string(str)); } memcpy((&m_val_storage[0]), str, len); m_val_storage[len] = 0; } break; case PT_BOOL: if(string(str) == "true") { *(uint32_t*)(&m_val_storage[0]) = 1; } else if(string(str) == "false") { *(uint32_t*)(&m_val_storage[0]) = 0; } else { throw sinsp_exception("filter error: unrecognized boolean value " + string(str)); } break; case PT_IPV4ADDR: if(inet_pton(AF_INET, str, (&m_val_storage[0])) != 1) { throw sinsp_exception("unrecognized IP address " + string(str)); } break; case PT_BYTEBUF: if(len >= m_val_storage.size()) { throw sinsp_exception("filter parameter too long:" + string(str)); } memcpy((&m_val_storage[0]), str, len); m_val_storage_len = len; break; default: ASSERT(false); throw sinsp_exception("wrong event type " + to_string((long long) m_field->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::Value::nullRef) { uint8_t* rawval = extract(evt, &len); if(rawval == NULL) { return Json::Value::nullRef; } return rawval_to_json(rawval, m_field, len); } return jsonval; } int32_t sinsp_filter_check::parse_field_name(const char* str, bool alloc_state) { int32_t j; int32_t max_fldlen = -1; 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; } } } return max_fldlen; } void sinsp_filter_check::parse_filter_value(const char* str, uint32_t len) { string_to_rawval(str, len, m_field->m_type); } const filtercheck_field_info* sinsp_filter_check::get_field_info() { return &m_info.m_fields[m_field_id]; } bool sinsp_filter_check::compare(sinsp_evt *evt) { uint32_t len; uint8_t* extracted_val = extract(evt, &len); if(extracted_val == NULL) { return false; } return flt_compare(m_cmpop, m_info.m_fields[m_field_id].m_type, extracted_val, &m_val_storage[0], 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]; } } 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; for(j = 0; j < size; j++) { sinsp_filter_check* chk = m_checks[j]; ASSERT(chk != NULL); if(j == 0) { switch(chk->m_boolop) { case BO_NONE: res = chk->compare(evt); break; case BO_NOT: res = !chk->compare(evt); break; default: ASSERT(false); break; } } else { switch(chk->m_boolop) { case BO_OR: res = res || chk->compare(evt); break; case BO_AND: res = res && chk->compare(evt); break; case BO_ORNOT: res = res || !chk->compare(evt); break; case BO_ANDNOT: res = res && !chk->compare(evt); break; default: ASSERT(false); break; } } } return res; } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter implementation /////////////////////////////////////////////////////////////////////////////// sinsp_filter::sinsp_filter(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_expression(); m_curexpr = m_filter; m_last_boolop = BO_NONE; m_nest_level = 0; try { compile(fltstr); } 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() { if(m_filter) { delete m_filter; } } bool sinsp_filter::isblank(char c) { if(c == ' ' || c == '\t' || c == '\n' || c == '\r') { return true; } else { return false; } } bool sinsp_filter::is_special_char(char c) { if(c == '(' || c == ')' || c == '!' || c == '=' || c == '<' || c == '>') { return true; } return false; } bool sinsp_filter::is_bracket(char c) { if(c == '(' || c == ')') { return true; } return false; } char sinsp_filter::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::next_operand(bool expecting_first_operand, bool in_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_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_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::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; } } ppm_cmp_operator sinsp_filter::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("in")) { m_scanpos += 2; return CO_IN; } 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::parse_check(sinsp_filter_expression* parent_expr, boolop op) { 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); 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)) { throw sinsp_exception("the given filter is not supported for thread table filtering"); } } ppm_cmp_operator co = next_comparison_operator(); chk->m_boolop = op; chk->m_cmpop = co; chk->parse_field_name((char *)&operand1[0], true); // // In this case we need to create '(field=value1 or field=value2 ...)' // if(co == CO_IN) { // // Separate the 'or's from the // rest of the conditions // push_expression(op); // // Skip spaces // if(isblank(m_fltstr[m_scanpos])) { next(); } if(m_fltstr[m_scanpos] != '(') { throw sinsp_exception("expected '(' after 'in' operand"); } // // Skip '(' // m_scanpos++; // // 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->parse_filter_value((char *)&operand2[0], (uint32_t)operand2.size() - 1); // // We pushed another expression before // so 'parent_expr' still referers to // the old one, this is the new nested // level for the 'or' sequence // m_curexpr->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 // pop_expression(); } 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->parse_filter_value((char *)&operand2[0], (uint32_t)operand2.size() - 1); } parent_expr->add_check(chk); } } void sinsp_filter::push_expression(boolop op) { sinsp_filter_expression* newexpr = new sinsp_filter_expression(); newexpr->m_boolop = op; newexpr->m_parent = m_curexpr; m_last_boolop = BO_NONE; m_curexpr->m_checks.push_back((sinsp_filter_check*)newexpr); m_curexpr = newexpr; m_nest_level++; } void sinsp_filter::pop_expression() { ASSERT(m_curexpr->m_parent != NULL); m_curexpr = m_curexpr->m_parent; m_nest_level--; } void sinsp_filter::compile(const string& fltstr) { m_fltstr = fltstr; 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; break; case '(': if(m_state != ST_NEED_EXPRESSION) { throw sinsp_exception("unexpected '(' after " + m_fltstr.substr(0, m_scanpos)); } push_expression(m_last_boolop); break; case ')': pop_expression(); break; case 'o': 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; break; case 'a': 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; 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_curexpr, m_last_boolop); 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, ' '); } bool sinsp_filter::run(sinsp_evt *evt) { return m_filter->compare(evt); } #endif // HAS_FILTERING sysdig-0.8.0/userspace/libsinsp/filter.h000066400000000000000000000045731265472057500203220ustar00rootroot00000000000000/* 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_FILTERING class sinsp_filter_expression; enum boolop { BO_NONE = 0, BO_NOT = 1, BO_OR = 2, BO_AND = 4, BO_ORNOT = 3, BO_ANDNOT = 5, }; /** @defgroup filter Filtering events * Filtering infrastructure. * @{ */ /*! \brief This is the class that compiles and 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. \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(sinsp* inspector, const string& fltstr, bool ttable_only=false); ~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); private: enum state { ST_EXPRESSION_DONE, ST_NEED_EXPRESSION, }; char next(); bool compare_no_consume(const string& str); vector next_operand(bool expecting_first_operand, bool in_clause); ppm_cmp_operator next_comparison_operator(); void parse_check(sinsp_filter_expression* parent_expr, boolop op); void push_expression(boolop op); void pop_expression(); void compile(const string& fltstr); 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; sinsp_filter_expression* m_curexpr; boolop m_last_boolop; int32_t m_nest_level; sinsp_filter_expression* m_filter; friend class sinsp_evt_formatter; }; /*@}*/ #endif // HAS_FILTERING sysdig-0.8.0/userspace/libsinsp/filterchecks.cpp000066400000000000000000003554041265472057500220400ustar00rootroot00000000000000/* 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" #ifdef HAS_FILTERING #include "filter.h" #include "filterchecks.h" #include "protodecoder.h" extern sinsp_evttables g_infotables; int32_t g_csysdig_screen_w = -1; /////////////////////////////////////////////////////////////////////////////// // 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 uknown."}, {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."} }; 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) { 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; m_tstr.erase(remove_if(m_tstr.begin(), m_tstr.end(), g_invalidchar()), m_tstr.end()); return true; } default: m_tstr = ""; return true; } } uint8_t* sinsp_filter_check_fd::extract_from_null_fd(sinsp_evt *evt, OUT uint32_t* len) { // // 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) == true) { return (uint8_t*)m_tstr.c_str(); } else { return NULL; } } case TYPE_CONTAINERNAME: { if(extract_fdname_from_creator(evt, len) == true) { m_tstr = m_tinfo->m_container_id + ':' + m_tstr; return (uint8_t*)m_tstr.c_str(); } else { return NULL; } } case TYPE_DIRECTORY: { if(extract_fdname_from_creator(evt, len) == true) { m_tstr.erase(remove_if(m_tstr.begin(), m_tstr.end(), g_invalidchar()), m_tstr.end()); size_t pos = m_tstr.rfind('/'); if(pos != string::npos) { if(pos < m_tstr.size() - 1) { m_tstr.resize(pos); } } else { m_tstr = "/"; } return (uint8_t*)m_tstr.c_str(); } else { return NULL; } } case TYPE_CONTAINERDIRECTORY: { if(extract_fdname_from_creator(evt, len) == true) { m_tstr.erase(remove_if(m_tstr.begin(), m_tstr.end(), g_invalidchar()), m_tstr.end()); 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; 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) == true) { m_tstr.erase(remove_if(m_tstr.begin(), m_tstr.end(), g_invalidchar()), m_tstr.end()); size_t pos = m_tstr.rfind('/'); if(pos != string::npos) { if(pos < m_tstr.size() - 1) { m_tstr = m_tstr.substr(pos + 1, string::npos); } } return (uint8_t*)m_tstr.c_str(); } else { return NULL; } } case TYPE_FDTYPECHAR: 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) { 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); } 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); } } 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; } m_tstr.erase(remove_if(m_tstr.begin(), m_tstr.end(), g_invalidchar()), m_tstr.end()); return (uint8_t*)m_tstr.c_str(); case TYPE_FDTYPE: if(m_fdinfo == NULL) { return NULL; } return (uint8_t*)m_fdinfo->get_typestring(); case TYPE_DIRECTORY: case TYPE_CONTAINERDIRECTORY: { if(m_fdinfo == NULL) { return extract_from_null_fd(evt, len); } if(!(m_fdinfo->is_file() || m_fdinfo->is_directory())) { return NULL; } m_tstr = m_fdinfo->m_name; m_tstr.erase(remove_if(m_tstr.begin(), m_tstr.end(), g_invalidchar()), m_tstr.end()); 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; } return (uint8_t*)m_tstr.c_str(); } case TYPE_FILENAME: { if(m_fdinfo == NULL) { return extract_from_null_fd(evt, len); } if(!m_fdinfo->is_file()) { return NULL; } m_tstr = m_fdinfo->m_name; m_tstr.erase(remove_if(m_tstr.begin(), m_tstr.end(), g_invalidchar()), m_tstr.end()); size_t pos = m_tstr.rfind('/'); if(pos != string::npos) { if(pos < m_tstr.size() - 1) { m_tstr = m_tstr.substr(pos + 1, string::npos); } } else { m_tstr = "/"; } return (uint8_t*)m_tstr.c_str(); } case TYPE_FDTYPECHAR: if(m_fdinfo == NULL) { return extract_from_null_fd(evt, len); } m_tcstr[0] = m_fdinfo->get_typechar(); m_tcstr[1] = 0; return m_tcstr; 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_SERVERIP: { 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_dip); } else if(evt_type == SCAP_FD_IPV4_SERVSOCK) { return (uint8_t*)&(m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_ip); } } break; 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)) { 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); } 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); } 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)) { 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)) { 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); } 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; } 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); } 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_tstr = "ip"; return (uint8_t*)m_tstr.c_str(); } else if(m_fdinfo->m_type == SCAP_FD_UNIX_SOCK) { m_tstr = "unix"; 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); 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) { if(flt_compare(m_cmpop, PT_IPV4ADDR, &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip, &m_val_storage[0]) || flt_compare(m_cmpop, PT_IPV4ADDR, &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip, &m_val_storage[0])) { 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, &m_val_storage[0]) && flt_compare(m_cmpop, PT_IPV4ADDR, &m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip, &m_val_storage[0])) { return true; } } else { throw sinsp_exception("filter error: IP filter only supports '=' and '!=' operators"); } } else if(evt_type == SCAP_FD_IPV4_SERVSOCK) { if(m_fdinfo->m_sockinfo.m_ipv4serverinfo.m_ip == *(uint32_t*)&m_val_storage[0]) { 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*)&m_val_storage[0] || *dport == *(uint16_t*)&m_val_storage[0]) { return true; } break; case CO_NE: if(*sport != *(uint16_t*)&m_val_storage[0] && *dport != *(uint16_t*)&m_val_storage[0]) { return true; } break; case CO_LT: if(*sport < *(uint16_t*)&m_val_storage[0] || *dport < *(uint16_t*)&m_val_storage[0]) { return true; } break; case CO_LE: if(*sport <= *(uint16_t*)&m_val_storage[0] || *dport <= *(uint16_t*)&m_val_storage[0]) { return true; } break; case CO_GT: if(*sport > *(uint16_t*)&m_val_storage[0] || *dport > *(uint16_t*)&m_val_storage[0]) { return true; } break; case CO_GE: if(*sport >= *(uint16_t*)&m_val_storage[0] || *dport >= *(uint16_t*)&m_val_storage[0]) { 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_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) { // // A couple of 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); } // // Standard extract-based fields // uint32_t len; uint8_t* extracted_val = extract(evt, &len); if(extracted_val == NULL) { return false; } return flt_compare(m_cmpop, m_info.m_fields[m_field_id].m_type, extracted_val, &m_val_storage[0]); } /////////////////////////////////////////////////////////////////////////////// // 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_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."}, }; 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) { 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); } 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); } else { return sinsp_filter_check::parse_field_name(str, alloc_state); } } 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; } uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len) { 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_NAME: m_tstr = tinfo->get_comm(); return (uint8_t*)m_tstr.c_str(); case TYPE_EXE: m_tstr = tinfo->get_exe(); 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 += ' '; } } return (uint8_t*)m_tstr.c_str(); } case TYPE_ENV: { m_tstr.clear(); uint32_t j; uint32_t nargs = (uint32_t)tinfo->m_env.size(); for(j = 0; j < nargs; j++) { m_tstr += tinfo->m_env[j]; if(j < nargs -1) { m_tstr += ' '; } } return (uint8_t*)m_tstr.c_str(); } case TYPE_CMDLINE: { m_tstr = tinfo->get_comm() + " "; uint32_t j; uint32_t nargs = (uint32_t)tinfo->m_args.size(); for(j = 0; j < nargs; j++) { m_tstr += tinfo->m_args[j]; if(j < nargs -1) { m_tstr += ' '; } } return (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 += ' '; } } return (uint8_t*)m_tstr.c_str(); } case TYPE_CWD: m_tstr = tinfo->get_cwd(); 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(); 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(); 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; } } for(; mt != NULL; mt = mt->get_parent_thread()) { size_t len = mt->m_comm.size(); if(len >= 2 && mt->m_comm[len - 2] == 's' && mt->m_comm[len - 1] == 'h') { res = &mt->m_pid; } } 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 += ' '; } } 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; 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); } default: ASSERT(false); return NULL; } } bool sinsp_filter_check_thread::compare_full_apid(sinsp_evt *evt) { bool res; uint32_t j; 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 // for(j = 0; mt != NULL; mt = mt->get_parent_thread(), j++) { if(j > 0) { res = flt_compare(m_cmpop, PT_PID, &mt->m_pid, &m_val_storage[0]); if(res == true) { return true; } } } return false; } bool sinsp_filter_check_thread::compare_full_aname(sinsp_evt *evt) { bool res; uint32_t j; 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 // for(j = 0; mt != NULL; mt = mt->get_parent_thread(), j++) { if(j > 0) { res = flt_compare(m_cmpop, PT_CHARBUF, (void*)mt->m_comm.c_str(), &m_val_storage[0]); if(res == true) { return true; } } } return false; } 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."} }; 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(); } sinsp_filter_check_event::~sinsp_filter_check_event() { 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) { string val(str); // // 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]; return 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; int32_t res = extract_arg("evt.rawarg", val, &m_arginfo); m_customfield.m_type = m_arginfo->type; return res; } 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]; return 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)); } return sinsp_filter_check::parse_field_name(str, alloc_state); } 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"); } return (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]; return extract_type("evt.type.is", val, NULL); } else { return sinsp_filter_check::parse_field_name(str, alloc_state); } } void sinsp_filter_check_event::parse_filter_value(const char* str, uint32_t len) { string val(str); if(m_field_id == TYPE_ARGRAW) { // // 'rawarg' is handled in a custom way // ASSERT(m_arginfo != NULL); return sinsp_filter_check::string_to_rawval(str, len, m_arginfo->type); } else 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 sinsp_filter_check::parse_filter_value(str, len); } } for(uint32_t j = 0; j < PPM_SC_MAX; j++) { if(stype == stable[j].name) { return sinsp_filter_check::parse_filter_value(str, len); } } 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"); } sinsp_filter_check::parse_filter_value(str, len); m_tsdelta = sinsp_numparser::parseu64(str) * 1000000; return; } else { return sinsp_filter_check::parse_filter_value(str, len); } } 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]; } } int32_t sinsp_filter_check_event::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 sinsp_filter_check_event::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; } 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; 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::Value::nullRef; } return Json::Value::nullRef; } 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_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) { switch(m_field_id) { case TYPE_TIME: ts_to_string(evt->get_ts(), &m_strstorage, false, true); return (uint8_t*)m_strstorage.c_str(); case TYPE_TIME_S: ts_to_string(evt->get_ts(), &m_strstorage, false, false); return (uint8_t*)m_strstorage.c_str(); case TYPE_DATETIME: ts_to_string(evt->get_ts(), &m_strstorage, true, true); 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); } 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); 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); 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); return (uint8_t*) m_strstorage.c_str(); case 'd': { if(evt->m_tinfo != NULL) { uint64_t lat = evt->m_tinfo->m_latency; m_strstorage += to_string(lat / 1000000000); m_strstorage += "."; snprintf(timebuffer, sizeof(timebuffer), "%09lu", lat % 1000000000); m_strstorage += string(timebuffer); } else { m_strstorage = "0.000000000"; } 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; return (uint8_t*) m_strstorage.c_str(); } } case TYPE_DIR: if(PPME_IS_ENTER(evt->get_type())) { return (uint8_t*)">"; } else { 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(); } 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(); } 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; } 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) { return (uint8_t*)resolved_argstr; } else { 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)) { 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 // 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 + ") "; } } 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_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) { return (uint8_t*)resolved_argstr; } else if(argstr != NULL) { return (uint8_t*)argstr; } } } else { if((evt->get_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) { return (uint8_t*)resolved_argstr; } else if(argstr != NULL) { 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_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_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_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_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_flags(); if(eflags & EF_WRITES_TO_FD) { m_strstorage = "write"; } else if(eflags & EF_READS_FROM_FD) { m_strstorage = "read"; } else { return NULL; } return (uint8_t*)m_strstorage.c_str(); } case TYPE_ISWAIT: { ppm_event_flags eflags = evt->get_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_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_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_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_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) { 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) { 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) { 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) || etype == SCAP_FD_UNIX_SOCK) { 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) || etype == SCAP_FD_UNIX_SOCK) { 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) || etype == SCAP_FD_UNIX_SOCK) { return extract_buflen(evt); } } 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; uint8_t* extracted_val = extract(evt, &len); if(extracted_val == NULL) { return false; } ASSERT(m_arginfo != NULL); res = flt_compare(m_cmpop, m_arginfo->type, extracted_val, &m_val_storage[0]); } 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) { 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: return (uint8_t*)uinfo->name; case TYPE_HOMEDIR: return (uint8_t*)uinfo->homedir; case TYPE_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) { 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); return (uint8_t*)ginfo->name; } default: ASSERT(false); break; } return NULL; } /////////////////////////////////////////////////////////////////////////////// // 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) { ASSERT(false); return -1; } void rawstring_check::parse_filter_value(const char* str, uint32_t len) { ASSERT(false); } uint8_t* rawstring_check::extract(sinsp_evt *evt, OUT uint32_t* len) { *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) { int32_t res = sinsp_filter_check::parse_field_name(str, alloc_state); 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) { 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: return (uint8_t*)m_decoder->get_facility_str(); case TYPE_SEVERITY: return (uint8_t*)&m_decoder->m_severity; case TYPE_SEVERITY_STR: return (uint8_t*)m_decoder->get_severity_str(); case TYPE_MESSAGE: 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."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.type", "the container type, eg: docker or rkt"} }; 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(); } uint8_t* sinsp_filter_check_container::extract(sinsp_evt *evt, OUT uint32_t* len) { sinsp_threadinfo* tinfo = evt->get_thread_info(); if(tinfo == NULL) { return NULL; } switch(m_field_id) { case TYPE_CONTAINER_ID: if(tinfo->m_container_id.empty()) { m_tstr = "host"; } else { m_tstr = tinfo->m_container_id; } return (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; } 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; } 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; } } return (uint8_t*)m_tstr.c_str(); 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) { ASSERT(false); return -1; } void sinsp_filter_check_reference::parse_filter_value(const char* str, uint32_t len) { ASSERT(false); } uint8_t* sinsp_filter_check_reference::extract(sinsp_evt *evt, OUT uint32_t* len) { *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); } } /////////////////////////////////////////////////////////////////////////////// // 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) { 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) { 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); } return (uint8_t*)m_strval.c_str(); } else { return NULL; } } /////////////////////////////////////////////////////////////////////////////// // 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'."}, }; 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) { 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.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 { return sinsp_filter_check::parse_field_name(str, alloc_state); } } 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; } 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; } 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) { if(m_inspector->m_k8s_client == NULL) { return NULL; } ASSERT(evt); if(evt == NULL) { ASSERT(false); return NULL; } sinsp_threadinfo* tinfo = evt->get_thread_info(); if(tinfo == NULL) { return NULL; } const k8s_pod_t* pod = find_pod_for_thread(tinfo); if(pod == NULL) { return NULL; } m_tstr.clear(); switch(m_field_id) { case TYPE_K8S_POD_NAME: m_tstr = pod->get_name(); return (uint8_t*) m_tstr.c_str(); case TYPE_K8S_POD_ID: m_tstr = pod->get_uid(); return (uint8_t*) m_tstr.c_str(); case TYPE_K8S_POD_LABEL: { if(find_label(pod->get_labels(), m_argname, &m_tstr)) { return (uint8_t*) m_tstr.c_str(); } break; } case TYPE_K8S_POD_LABELS: { concatenate_labels(pod->get_labels(), &m_tstr); 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(); 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(); 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)) { 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); 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()); } 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()); } 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()) { 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); } return (uint8_t*) m_tstr.c_str(); } break; } case TYPE_K8S_NS_NAME: { m_tstr = pod->get_namespace(); 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(); 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)) { 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); return (uint8_t*) m_tstr.c_str(); } break; } default: ASSERT(false); return NULL; } return NULL; } #endif // HAS_FILTERING sysdig-0.8.0/userspace/libsinsp/filterchecks.h000066400000000000000000000414521265472057500215000ustar00rootroot00000000000000/* 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 "k8s.h" #ifdef HAS_FILTERING class sinsp_filter_check_reference; #define VALIDATE_STR_VAL if(val.length() >= sizeof(m_val_storage)) \ { \ throw sinsp_exception("filter error: value too long: " + val); \ } bool flt_compare(ppm_cmp_operator op, ppm_param_type type, void* operand1, void* operand2, uint32_t op1_len = 0, uint32_t op2_len = 0); bool flt_compare_avg(ppm_cmp_operator op, ppm_param_type type, void* operand1, void* operand2, uint32_t op1_len, uint32_t op2_len, uint32_t cnt1, uint32_t cnt2); char* flt_to_string(uint8_t* rawval, filtercheck_field_info* finfo); 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); // // 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. // virtual void parse_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 // virtual uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len) = 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::Value::nullRef; } // // 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); sinsp* m_inspector; boolop m_boolop; ppm_cmp_operator m_cmpop; sinsp_field_aggregation m_aggregation; sinsp_field_aggregation m_merge_aggregation; protected: 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_storage; const filtercheck_field_info* m_field; filter_check_info m_info; uint32_t m_field_id; uint32_t m_th_state_id; uint32_t m_val_storage_len; private: void set_inspector(sinsp* inspector); friend class sinsp_filter_check_list; }; // // Global class that stores the list of filtercheck plugins and offers // functions to work with it. // class sinsp_filter_check_list { public: sinsp_filter_check_list(); ~sinsp_filter_check_list(); void add_filter_check(sinsp_filter_check* filter_check); void get_all_fields(vector* list); sinsp_filter_check* new_filter_check_from_another(sinsp_filter_check *chk); sinsp_filter_check* new_filter_check_from_fldname(const string& name, sinsp* inspector, bool do_exact_check); private: vector m_check_list; }; /////////////////////////////////////////////////////////////////////////////// // Filter 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) { ASSERT(false); return 0; } void parse_filter_value(const char* str, uint32_t len) { ASSERT(false); } const filtercheck_field_info* get_field_info() { ASSERT(false); return NULL; } uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len) { ASSERT(false); return NULL; } 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 }; 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 compare_ip(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 extract_fdname_from_creator(sinsp_evt *evt, OUT uint32_t* len); bool extract_fd(sinsp_evt *evt); }; // // thread sinsp_filter_check_syslog // 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_APID = 12, TYPE_ANAME = 13, TYPE_LOGINSHELLID = 14, TYPE_DURATION = 15, TYPE_FDOPENCOUNT = 16, TYPE_FDLIMIT = 17, TYPE_FDUSAGE = 18, TYPE_VMSIZE = 19, TYPE_VMRSS = 20, TYPE_VMSWAP = 21, TYPE_PFMAJOR = 22, TYPE_PFMINOR = 23, TYPE_TID = 24, TYPE_ISMAINTHREAD = 25, TYPE_EXECTIME = 26, TYPE_TOTEXECTIME = 27, TYPE_CGROUPS = 28, TYPE_CGROUP = 29, TYPE_VTID = 30, TYPE_VPID = 31, TYPE_THREAD_CPU = 32, TYPE_THREAD_CPU_USER = 33, TYPE_THREAD_CPU_SYSTEM = 34, TYPE_THREAD_VMSIZE = 35, TYPE_THREAD_VMRSS = 36, TYPE_THREAD_VMSIZE_B = 37, TYPE_THREAD_VMRSS_B = 38, }; sinsp_filter_check_thread(); sinsp_filter_check* allocate_new(); int32_t parse_field_name(const char* str, bool alloc_state); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len); 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, }; sinsp_filter_check_event(); ~sinsp_filter_check_event(); sinsp_filter_check* allocate_new(); int32_t parse_field_name(const char* str, bool alloc_state); void parse_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); 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); int32_t gmt2local(time_t t); void ts_to_string(uint64_t ts, OUT string* res, bool full, bool ns); uint8_t *extract_abspath(sinsp_evt *evt, OUT uint32_t *len); inline uint8_t* extract_buflen(sinsp_evt *evt); bool m_is_compare; 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); 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); uint32_t m_gid; string m_name; }; // // 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); void parse_filter_value(const char* str, uint32_t len); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len); // 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); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len); 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_TYPE }; sinsp_filter_check_container(); sinsp_filter_check* allocate_new(); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len); private: string m_tstr; }; // // 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); void parse_filter_value(const char* str, uint32_t len); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len); char* tostring_nice(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); 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); private: string m_strval; char m_addrbuff[100]; }; 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, }; sinsp_filter_check_k8s(); sinsp_filter_check* allocate_new(); int32_t parse_field_name(const char* str, bool alloc_state); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len); 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); vector find_svc_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_FILTERING sysdig-0.8.0/userspace/libsinsp/ifinfo.cpp000066400000000000000000000125571265472057500206430ustar00rootroot00000000000000/* 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) { 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.8.0/userspace/libsinsp/ifinfo.h000066400000000000000000000042231265472057500202770ustar00rootroot00000000000000/* 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: 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); 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; }; void sinsp_network_interfaces::clear() { m_ipv4_interfaces.clear(); m_ipv6_interfaces.clear(); }sysdig-0.8.0/userspace/libsinsp/ifinfo_test.cpp000066400000000000000000000075561265472057500217050ustar00rootroot00000000000000/* 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.8.0/userspace/libsinsp/internal_metrics.cpp000066400000000000000000000016521265472057500227250ustar00rootroot00000000000000/* 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.8.0/userspace/libsinsp/internal_metrics.h000066400000000000000000000051271265472057500223730ustar00rootroot00000000000000/* 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.8.0/userspace/libsinsp/k8s.cpp000066400000000000000000000250241265472057500200670ustar00rootroot00000000000000// // 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 const k8s_component::component_map k8s::m_components = { { k8s_component::K8S_NODES, "nodes" }, { k8s_component::K8S_NAMESPACES, "namespaces" }, { k8s_component::K8S_PODS, "pods" }, { k8s_component::K8S_REPLICATIONCONTROLLERS, "replicationcontrollers" }, { k8s_component::K8S_SERVICES, "services" } }; #ifdef K8S_DISABLE_THREAD k8s::dispatch_map k8s::make_dispatch_map(k8s_state_t& state) { return dispatch_map { { k8s_component::K8S_NODES, new k8s_dispatcher(k8s_component::K8S_NODES, state)}, { k8s_component::K8S_NAMESPACES, new k8s_dispatcher(k8s_component::K8S_NAMESPACES, state)}, { k8s_component::K8S_PODS, new k8s_dispatcher(k8s_component::K8S_PODS, state)}, { k8s_component::K8S_REPLICATIONCONTROLLERS, new k8s_dispatcher(k8s_component::K8S_REPLICATIONCONTROLLERS, state)}, { k8s_component::K8S_SERVICES, new k8s_dispatcher(k8s_component::K8S_SERVICES, state)} }; } #else k8s::dispatch_map k8s::make_dispatch_map(k8s_state_t& state, std::mutex& mut) { return dispatch_map { { k8s_component::K8S_NODES, new k8s_dispatcher(k8s_component::K8S_NODES, state, mut)}, { k8s_component::K8S_NAMESPACES, new k8s_dispatcher(k8s_component::K8S_NAMESPACES, state, mut)}, { k8s_component::K8S_PODS, new k8s_dispatcher(k8s_component::K8S_PODS, state, mut)}, { k8s_component::K8S_REPLICATIONCONTROLLERS, new k8s_dispatcher(k8s_component::K8S_REPLICATIONCONTROLLERS, state, mut)}, { k8s_component::K8S_SERVICES, new k8s_dispatcher(k8s_component::K8S_SERVICES, state, mut)} }; } #endif // K8S_DISABLE_THREAD k8s::k8s(const std::string& uri, bool start_watch, bool watch_in_thread, bool is_captured, const std::string& api, const std::string& cert) : m_watch(uri.empty() ? false : start_watch), m_watch_in_thread(uri.empty() ? false : start_watch && watch_in_thread), m_state(is_captured), #ifndef K8S_DISABLE_THREAD m_dispatch(std::move(make_dispatch_map(m_state, m_mutex))), #else m_dispatch(std::move(make_dispatch_map(m_state))) #endif #ifdef HAS_CAPTURE ,m_net(uri.empty() ? 0 : new k8s_net(*this, uri, api, cert)) #endif { if (!uri.empty()) { #ifdef K8S_DISABLE_THREAD if(watch_in_thread) { g_logger.log("Watching in thread requested but not available (only available in multi-thread build).", sinsp_logger::SEV_WARNING); } #endif // K8S_DISABLE_THREAD try { get_state(true); } catch (...) { cleanup(); throw; } if(m_watch) { watch(); } } } k8s::~k8s() { stop_watch(); cleanup(); } void k8s::stop_watch() { #ifdef HAS_CAPTURE if(m_watch) { ASSERT(m_net); m_net->stop_watching(); } #endif } void k8s::cleanup() { for (auto& update : m_dispatch) { delete update.second; } #ifdef HAS_CAPTURE delete m_net; #endif } void k8s::build_state() { #ifdef HAS_CAPTURE std::ostringstream os; for (auto& component : m_components) { { K8S_LOCK_GUARD_MUTEX; m_state.clear(component.first); } ASSERT(m_net); m_net->get_all_data(component, os); parse_json(os.str(), component); os.str(""); } #endif } const k8s_state_t& k8s::get_state(bool rebuild) { try { if(rebuild) { build_state(); } } catch (std::exception& ex) { g_logger.log(ex.what(), sinsp_logger::SEV_ERROR); throw; } return m_state; } void k8s::watch() { #ifdef HAS_CAPTURE ASSERT(m_net); if((m_watch && !m_net->is_watching()) || !m_watch_in_thread) { m_net->watch(); } #endif } void k8s::stop_watching() { #ifdef HAS_CAPTURE ASSERT(m_net); if(m_net->is_watching()) { m_net->stop_watching(); } #endif } void k8s::on_watch_data(k8s_event_data&& msg) { m_dispatch[msg.component()]->enqueue(std::move(msg)); } void k8s::simulate_watch_event(const std::string& json) { Json::Value root; Json::Reader reader; k8s_component::type component_type = k8s_component::K8S_COMPONENT_COUNT; if(reader.parse(json, root, false)) { 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 == "Service") { component_type = k8s_component::K8S_SERVICES; } 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; } ASSERT(component_type < k8s_component::K8S_COMPONENT_COUNT); m_dispatch[component_type]->extract_data(json, false); } std::size_t k8s::count(k8s_component::type component) const { K8S_LOCK_GUARD_MUTEX; 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_SERVICES: return m_state.get_services().size(); case k8s_component::K8S_COMPONENT_COUNT: default: break; } std::ostringstream os; os << "Unknown component " << static_cast(component); throw sinsp_exception(os.str()); } void k8s::extract_data(Json::Value& items, k8s_component::type component, const std::string& api_version) { if(api_version.empty()) { throw sinsp_exception("API version not provided."); } std::ostringstream os; const std::string event_type = "ADDED"; std::string component_kind, component_name, component_uid, component_ns; if(items.isArray()) { K8S_LOCK_GUARD_MUTEX; for (auto& item : items) { Json::Value metadata = item["metadata"]; if(!metadata.isNull()) { Json::Value ns = metadata["namespace"]; std::string nspace; if(!ns.isNull()) { nspace = std::move(ns.asString()); } m_state.add_common_single_value(component, metadata["name"].asString(), metadata["uid"].asString(), nspace); std::vector entries = k8s_component::extract_object(metadata, "labels"); if(entries.size() > 0) { m_state.replace_items(component, "labels", std::move(entries)); } } Json::Value spec = item["spec"]; if(!spec.isNull()) { std::vector entries = k8s_component::extract_object(spec, "selector"); if(entries.size() > 0) { m_state.replace_items(component, "selector", std::move(entries)); } } component_kind.clear(); component_name.clear(); component_uid.clear(); switch(component) { case k8s_component::K8S_NAMESPACES: { const k8s_namespaces& nss = m_state.get_namespaces(); if(nss.size()) { component_kind = "Namespace"; component_name = nss.back().get_name(); component_uid = nss.back().get_uid(); } } break; case k8s_component::K8S_NODES: { Json::Value status = item["status"]; if(!status.isNull()) { const k8s_nodes& nds = m_state.get_nodes(); if(nds.size()) { component_kind = "Node"; component_name = nds.back().get_name(); component_uid = nds.back().get_uid(); std::vector addresses = k8s_component::extract_nodes_addresses(status); for (auto&& address : addresses) { m_state.add_last_node_ip(std::move(address)); } } } } break; case k8s_component::K8S_PODS: { k8s_pods& p = m_state.get_pods(); if(p.size()) { component_kind = "Pod"; component_name = p.back().get_name(); component_uid = p.back().get_uid(); component_ns = p.back().get_namespace(); k8s_pod_t& pod = p.back(); m_state.update_pod(pod, item, true); } } break; case k8s_component::K8S_SERVICES: if(!spec.isNull()) { k8s_services& svcs = m_state.get_services(); if(svcs.size()) { component_kind = "Service"; component_name = svcs.back().get_name(); component_uid = svcs.back().get_uid(); component_ns = svcs.back().get_namespace(); k8s_component::extract_services_data(spec, svcs.back(), m_state.get_pods()); } } break; case k8s_component::K8S_REPLICATIONCONTROLLERS: { const k8s_controllers& rcs = m_state.get_rcs(); if(rcs.size()) { component_kind = "ReplicationController"; component_name = rcs.back().get_name(); component_uid = rcs.back().get_uid(); } break; } default: break; } os.str(""); os << '[' << event_type << ',' << component_kind << ',' << component_name << ',' << component_uid << ',' << component_ns << ']'; g_logger.log(os.str(), sinsp_logger::SEV_INFO); #ifdef HAS_CAPTURE ASSERT(!component_kind.empty()); item["apiVersion"] = api_version; item["kind"] = component_kind; Json::Value new_item; new_item["type"] = event_type; new_item["object"] = item; new_item["apiVersion"] = api_version; new_item["kind"] = component_kind; m_state.enqueue_capture_event(new_item); #endif // HAS_CAPTURE } } } void k8s::parse_json(const std::string& json, const k8s_component::component_map::value_type& component) { Json::Value root; Json::Reader reader; if(reader.parse(json, root, false)) { Json::Value items = root["items"]; if(!items.isNull()) { Json::Value api_version = root["apiVersion"]; std::string api_ver = api_version.isNull() ? std::string() : api_version.asString(); extract_data(items, component.first, api_ver); //g_logger.log(root.toStyledString(), sinsp_logger::SEV_DEBUG); { K8S_LOCK_GUARD_MUTEX; m_state.update_cache(component.first); } } else { throw sinsp_exception("Invalid JSON"); } } else { throw sinsp_exception("JSON parsing failed"); } } sysdig-0.8.0/userspace/libsinsp/k8s.h000066400000000000000000000044071265472057500175360ustar00rootroot00000000000000// // 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 #include class k8s_dispatcher; class k8s { public: k8s(const std::string& uri = "http://localhost:80", bool start_watch = false, bool watch_in_thread = false, bool is_captured = false, const std::string& api = "/api/v1/", const std::string& cert = ""); ~k8s(); std::size_t count(k8s_component::type component) const; void on_watch_data(k8s_event_data&& msg); const k8s_state_t& get_state(bool rebuild = false); bool watch_in_thread() 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 void simulate_watch_event(const std::string& json); private: void extract_data(Json::Value& items, k8s_component::type component, const std::string& api_version); void build_state(); void parse_json(const std::string& json, const k8s_component::component_map::value_type& component); void stop_watch(); void cleanup(); // due to deleted default dispatcher constructor, g++ has trouble instantiating map with values, // so we have to go with the forward declaration above and pointers here ... typedef std::map dispatch_map; #ifdef K8S_DISABLE_THREAD static dispatch_map make_dispatch_map(k8s_state_t& state); #else static dispatch_map make_dispatch_map(k8s_state_t& state, std::mutex& mut); #endif // K8S_DISABLE_THREAD K8S_DECLARE_MUTEX; bool m_watch; bool m_watch_in_thread; k8s_state_t m_state; dispatch_map m_dispatch; #ifdef HAS_CAPTURE k8s_net* m_net; #endif static const k8s_component::component_map m_components; friend class k8s_test; }; inline bool k8s::watch_in_thread() const { return m_watch_in_thread; } inline bool k8s::is_alive() const { #ifdef HAS_CAPTURE ASSERT(m_net); return m_net->is_healthy() && m_net->is_watching(); #endif return true; } sysdig-0.8.0/userspace/libsinsp/k8s_collector.cpp000066400000000000000000000055211265472057500221350ustar00rootroot00000000000000// // k8s_collector.cpp // #ifdef HAS_CAPTURE #include "sinsp.h" #include "sinsp_int.h" #include "k8s_collector.h" #include "k8s_http.h" #include #include #include k8s_collector::k8s_collector(bool do_loop, long timeout_ms): m_subscription_count(0), m_nfds(0), m_loop(do_loop), m_timeout_ms(timeout_ms), m_stopped(false) { clear(); } k8s_collector::~k8s_collector() { } void k8s_collector::clear() { FD_ZERO(&m_errfd); FD_ZERO(&m_infd); } void k8s_collector::add(k8s_http* handler) { K8S_LOCK_GUARD_MUTEX; int sockfd = handler->get_watch_socket(5000L); FD_SET(sockfd, &m_errfd); FD_SET(sockfd, &m_infd); if(sockfd > m_nfds) { m_nfds = sockfd; } m_sockets[sockfd] = handler; m_subscription_count = m_sockets.size(); } void k8s_collector::remove(socket_map_t::iterator it) { if(it != m_sockets.end()) { m_sockets.erase(it); } m_nfds = 0; for (auto& sock : m_sockets) { if(sock.first > m_nfds) { m_nfds = sock.first; } } m_subscription_count = m_sockets.size(); } void k8s_collector::remove_all() { K8S_LOCK_GUARD_MUTEX; clear(); for (socket_map_t::iterator it = m_sockets.begin(); it != m_sockets.end();) { remove(it++); } } bool k8s_collector::is_active() const { K8S_LOCK_GUARD_MUTEX; return m_sockets.size() > 0; } int k8s_collector::subscription_count() const { return m_subscription_count; } void k8s_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; { K8S_LOCK_GUARD_MUTEX; if(m_sockets.size()) { res = select(m_nfds + 1, &m_infd, NULL, &m_errfd, &tv); if(res < 0) // error { std::string err = strerror(errno); g_logger.log(err, sinsp_logger::SEV_CRITICAL); remove_all(); } else // data or idle { for (auto& sock : m_sockets) { if(FD_ISSET(sock.first, &m_infd)) { if(!sock.second->on_data()) { remove(m_sockets.find(sock.first)); } } else { FD_SET(sock.first, &m_infd); } if(FD_ISSET(sock.first, &m_errfd)) { std::string err = strerror(errno); g_logger.log(err, sinsp_logger::SEV_CRITICAL); sock.second->on_error(err, true); remove(m_sockets.find(sock.first)); } else { FD_SET(sock.first, &m_errfd); } } } } else { g_logger.log("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("Collector error: ") + ex.what(), sinsp_logger::SEV_ERROR); remove_all(); m_stopped = true; } } #endif // HAS_CAPTURE sysdig-0.8.0/userspace/libsinsp/k8s_collector.h000066400000000000000000000015171265472057500216030ustar00rootroot00000000000000// // k8s_collector.h // #pragma once #ifdef HAS_CAPTURE #include "k8s_common.h" #include #include class k8s_http; class k8s_collector { public: typedef std::map socket_map_t; k8s_collector(bool do_loop = true, long timeout_ms = 1000L); ~k8s_collector(); void add(k8s_http* handler); void remove_all(); int subscription_count() const; void get_data(); void stop(); bool is_active() const; private: void clear(); void remove(socket_map_t::iterator it); socket_map_t m_sockets; std::atomic m_subscription_count; fd_set m_infd; fd_set m_errfd; int m_nfds; bool m_loop; long m_timeout_ms; bool m_stopped; K8S_DECLARE_MUTEX; }; inline void k8s_collector::stop() { m_stopped = true; } #endif // HAS_CAPTUREsysdig-0.8.0/userspace/libsinsp/k8s_common.h000066400000000000000000000011071265472057500211000ustar00rootroot00000000000000// // 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.8.0/userspace/libsinsp/k8s_component.cpp000066400000000000000000000277021265472057500221560ustar00rootroot00000000000000// // k8s_component.cpp // #include "k8s_component.h" #include "sinsp.h" #include "sinsp_int.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::component_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_SERVICES, "services" } }; k8s_component::k8s_component(const std::string& name, const std::string& uid, const std::string& ns) : m_name(name), m_uid(uid), m_ns(ns) { } k8s_pair_list k8s_component::extract_object(const Json::Value& object, const std::string& name) { k8s_pair_list entry_list; if(!object.isNull()) { Json::Value entries = object[name]; if(!entries.isNull()) { Json::Value::Members members = entries.getMemberNames(); for (auto& member : members) { Json::Value val = entries[member]; if(!val.isNull()) { entry_list.emplace_back(k8s_pair_t(member, val.asString())); } } } } return entry_list; } std::vector k8s_component::extract_pod_container_ids(const Json::Value& item) { std::vector container_list; Json::Value status = item["status"]; if(!status.isNull()) { Json::Value containers = status["containerStatuses"]; if(!containers.isNull()) { for (auto& container : containers) { Json::Value container_id = container["containerID"]; if(!container_id.isNull()) { container_list.emplace_back(container_id.asString()); } } } } return container_list; } k8s_container::list k8s_component::extract_pod_containers(const Json::Value& item) { k8s_container::list ext_containers; Json::Value spec = item["spec"]; if(!spec.isNull()) { Json::Value containers = spec["containers"]; if(!containers.isNull()) { for (auto& container : containers) { std::string cont_name; Json::Value name = container["name"]; if(!name.isNull()) { cont_name = name.asString(); } else { return ext_containers; } k8s_container::port_list cont_ports; Json::Value ports = container["ports"]; for(const auto& port : ports) { k8s_container::port cont_port; Json::Value name = port["name"]; if(!name.isNull()) { cont_port.set_name(name.asString()); } 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); } 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_component::extract_pod_data(const Json::Value& item, k8s_pod_t& pod) { Json::Value spec = item["spec"]; if(!spec.isNull()) { Json::Value node_name = spec["nodeName"]; if(!node_name.isNull()) { std::string nn = std::move(node_name.asString()); if(!nn.empty()) { pod.set_node_name(nn); } } Json::Value status = item["status"]; if(!status.isNull()) { Json::Value host_ip = status["hostIP"]; if(!host_ip.isNull()) { std::string hip = std::move(host_ip.asString()); if(!hip.empty()) { pod.set_host_ip(hip); } } Json::Value pod_ip = status["podIP"]; if(!pod_ip.isNull()) { std::string pip = std::move(pod_ip.asString()); if(!pip.empty()) { pod.set_internal_ip(pip); } } } } } std::vector k8s_component::extract_nodes_addresses(const Json::Value& status) { std::vector address_list; if(!status.isNull()) { 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") { Json::Value ip = address[entry]; if(!ip.isNull()) { address_list.emplace_back(std::move(ip.asString())); } } } } } } } return address_list; } void k8s_component::extract_services_data(const Json::Value& spec, k8s_service_t& service, const k8s_pods& pods) { if(!spec.isNull()) { Json::Value cluster_ip = spec["clusterIP"]; if(!cluster_ip.isNull()) { service.set_cluster_ip(cluster_ip.asString()); } k8s_service_t::port_list pl; Json::Value ports = spec["ports"]; if(!ports.isNull() && ports.isArray()) { for (auto& port : ports) { k8s_service_t::net_port p; Json::Value json_port = port["port"]; if(!json_port.isNull()) { p.m_port = json_port.asUInt(); } Json::Value json_protocol = port["protocol"]; if(!json_protocol.isNull()) { p.m_protocol = std::move(json_protocol.asString()); } 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 = std::move(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) { p.m_target_port = container_port->get_port(); break; } else { g_logger.log("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; } } 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); } else { // log warning } } } if(pl.size()) { service.set_port_list(std::move(pl)); } } } 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_SERVICES: return "services"; 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 == "services") { return K8S_SERVICES; } std::ostringstream os; os << "Unknown component name " << name; throw sinsp_exception(os.str().c_str()); } 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 { 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(); 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(name, uid, ns) { } // // node // k8s_node_t::k8s_node_t(const std::string& name, const std::string& uid, const std::string& ns) : k8s_component(name, uid, ns) { } // // pod // k8s_pod_t::k8s_pod_t(const std::string& name, const std::string& uid, const std::string& ns) : k8s_component(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; } // // replication controller // k8s_rc_t::k8s_rc_t(const std::string& name, const std::string& uid, const std::string& ns) : k8s_component(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; } // // service // k8s_service_t::k8s_service_t(const std::string& name, const std::string& uid, const std::string& ns) : k8s_component(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; } sysdig-0.8.0/userspace/libsinsp/k8s_component.h000066400000000000000000000327551265472057500216270ustar00rootroot00000000000000// // 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 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_COMPONENT_COUNT }; typedef std::pair component_pair; typedef std::map component_map; static const component_map list; k8s_component() = delete; k8s_component(const std::string& name, const std::string& uid, const std::string& ns = ""); 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); static std::vector extract_nodes_addresses(const Json::Value& status); // extracts labels or selectors static k8s_pair_list extract_object(const Json::Value& object, const std::string& name); 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 void extract_services_data(const Json::Value& spec, k8s_service_t& service, const std::vector& pods); 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); 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: 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; }; // // namespace // class k8s_ns_t : public k8s_component { public: 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::vector host_ip_list; 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 push_host_ip(const std::string& host_ip); void emplace_host_ip(std::string&& host_ip); 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; 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); // 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 const 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; }; // // replication controller // class k8s_rc_t : public k8s_component { public: k8s_rc_t(const std::string& name, const std::string& uid, const std::string& ns = ""); std::vector get_selected_pods(const std::vector& pods) const; }; // // 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; 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; }; typedef std::vector k8s_namespaces; typedef std::vector k8s_nodes; typedef std::vector k8s_pods; typedef std::vector k8s_controllers; typedef std::vector k8s_services; // // 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; } // // 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(m_host_ips.end(), host_ips.begin(), host_ips.end()); } inline void k8s_node_t::push_host_ip(const std::string& host_ip) { m_host_ips.push_back(host_ip); } inline void k8s_node_t::emplace_host_ip(std::string&& host_ip) { m_host_ips.emplace_back(std::move(host_ip)); } // // 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)); } // 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)); } inline const 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; } // // 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); } sysdig-0.8.0/userspace/libsinsp/k8s_dispatcher.cpp000066400000000000000000000355131265472057500223010ustar00rootroot00000000000000// // k8s_dispatcher.cpp // #include "k8s_dispatcher.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 #ifndef K8S_DISABLE_THREAD ,std::mutex& mut #endif ) : m_type(t), m_state(state) #ifndef K8S_DISABLE_THREAD ,m_mutex(mut) #endif { } 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))); data = data.substr(pos + 1); m_messages.push_back(""); msg = &m_messages.back(); }; 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()) { 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 = std::move(name.asString()); } Json::Value uid = meta["uid"]; if(!uid.isNull()) { data.m_uid = std::move(uid.asString()); } Json::Value nspace = meta["namespace"]; if(!nspace.isNull()) { data.m_namespace = std::move(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) { K8S_LOCK_GUARD_MUTEX; if(data.m_reason == COMPONENT_ADDED) { std::vector addresses = k8s_component::extract_nodes_addresses(root["status"]); 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_INFO); } k8s_node_t& node = m_state.get_component(m_state.get_nodes(), data.m_name, data.m_uid); if(addresses.size() > 0) { node.set_host_ips(std::move(addresses)); } Json::Value object = root["object"]; if(!object.isNull()) { 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) { std::vector addresses = k8s_component::extract_nodes_addresses(root["status"]); 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); if(addresses.size() > 0) { node.add_host_ips(std::move(addresses)); } Json::Value object = root["object"]; if(!object.isNull()) { 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 // COMPONENT_ERROR { log_error(root, "NODE"); } } void k8s_dispatcher::handle_namespace(const Json::Value& root, const msg_data& data) { K8S_LOCK_GUARD_MUTEX; 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_INFO); } k8s_ns_t& ns = m_state.get_component(m_state.get_namespaces(), data.m_name, data.m_uid); Json::Value object = root["object"]; if(!object.isNull()) { 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 node [" << 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); Json::Value object = root["object"]; if(!object.isNull()) { 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 // COMPONENT_ERROR { log_error(root, "NAMESPACE"); } } void k8s_dispatcher::handle_pod(const Json::Value& root, const msg_data& data) { K8S_LOCK_GUARD_MUTEX; if(data.m_reason == COMPONENT_ADDED) { 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_INFO); } 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, false); } } else if(data.m_reason == COMPONENT_MODIFIED) { 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; } 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, false); } } 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); } } else { g_logger.log(std::string("POD not found: ") + data.m_name, sinsp_logger::SEV_WARNING); } } else // COMPONENT_ERROR { log_error(root, "POD"); } } void k8s_dispatcher::handle_rc(const Json::Value& root, const msg_data& data) { K8S_LOCK_GUARD_MUTEX; if(data.m_reason == COMPONENT_ADDED) { if(m_state.has(m_state.get_rcs(), data.m_uid)) { std::ostringstream os; os << "ADDED message received for existing replication controller [" << data.m_uid << "], updating only."; g_logger.log(os.str(), sinsp_logger::SEV_INFO); } k8s_rc_t& rc = m_state.get_component(m_state.get_rcs(), data.m_name, data.m_uid, data.m_namespace); Json::Value object = root["object"]; if(!object.isNull()) { handle_labels(rc, object["metadata"], "labels"); handle_selectors(rc, object["spec"], "selector"); } } else if(data.m_reason == COMPONENT_MODIFIED) { if(!m_state.has(m_state.get_rcs(), data.m_uid)) { std::ostringstream os; os << "MODIFIED message received for non-existing replication controller [" << data.m_uid << "], giving up."; g_logger.log(os.str(), sinsp_logger::SEV_ERROR); return; } k8s_rc_t& rc = m_state.get_component(m_state.get_rcs(), data.m_name, data.m_uid, data.m_namespace); Json::Value object = root["object"]; if(!object.isNull()) { handle_labels(rc, object["metadata"], "labels"); handle_selectors(rc, object["spec"], "selector"); } } else if(data.m_reason == COMPONENT_DELETED) { if(!m_state.delete_component(m_state.get_rcs(), data.m_uid)) { g_logger.log(std::string("CONTROLLER not found: ") + data.m_name, sinsp_logger::SEV_ERROR); } } else // COMPONENT_ERROR { log_error(root, "REPLICATION CONTROLLER"); } } void k8s_dispatcher::handle_service(const Json::Value& root, const msg_data& data) { K8S_LOCK_GUARD_MUTEX; if(data.m_reason == COMPONENT_ADDED) { 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_INFO); } 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"); k8s_component::extract_services_data(object, service, m_state.get_pods()); } } else if(data.m_reason == COMPONENT_MODIFIED) { 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"); k8s_component::extract_services_data(object, service, m_state.get_pods()); } } 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 // COMPONENT_ERROR { log_error(root, "SERVICE"); } } void k8s_dispatcher::extract_data(const std::string& json, bool enqueue) { Json::Value root; Json::Reader reader; if(reader.parse(json, root, false)) { 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,"; handle_pod(root, data); break; case k8s_component::K8S_REPLICATIONCONTROLLERS: os << "REPLICATION_CONTROLLER,"; handle_rc(root, data); break; case k8s_component::K8S_SERVICES: os << "SERVICE,"; handle_service(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); { K8S_LOCK_GUARD_MUTEX; m_state.update_cache(m_type); #ifdef HAS_CAPTURE if(enqueue) { m_state.enqueue_capture_event(root); } #endif } } } else { g_logger.log("Bad JSON message received.", 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.8.0/userspace/libsinsp/k8s_dispatcher.h000066400000000000000000000054331265472057500217440ustar00rootroot00000000000000// // k8s_dispatcher.h // // kubernetes REST API notification abstraction // #pragma once #include "k8s_common.h" #include "k8s_component.h" #include "k8s_state.h" #include "k8s_event_data.h" #include "json/json.h" #include #include class k8s_dispatcher { public: 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 #ifndef K8S_DISABLE_THREAD ,std::mutex& mut #endif ); void enqueue(k8s_event_data&& data); void extract_data(const std::string& json, bool enqueue = false); 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); void handle_pod(const Json::Value& root, const msg_data& data); void handle_rc(const Json::Value& root, const msg_data& data); void handle_service(const Json::Value& root, const msg_data& data); // clears the content of labels and fills it with new values, if any template 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 template void handle_selectors(T& component, const Json::Value& spec, const std::string& name) { if(!spec.isNull()) { k8s_pair_list selectors = k8s_component::extract_object(spec, name); component.set_selectors(std::move(selectors)); } else { g_logger.log("Null spec object received", 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; #ifndef K8S_DISABLE_THREAD std::mutex& m_mutex; #endif }; inline const std::string& k8s_dispatcher::next_msg() { return m_messages.front(); } inline void k8s_dispatcher::remove() { m_messages.pop_front(); }sysdig-0.8.0/userspace/libsinsp/k8s_event_data.cpp000066400000000000000000000011551265472057500222600ustar00rootroot00000000000000// // 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.8.0/userspace/libsinsp/k8s_event_data.h000066400000000000000000000013101265472057500217160ustar00rootroot00000000000000// // 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.8.0/userspace/libsinsp/k8s_http.cpp000066400000000000000000000152751265472057500211350ustar00rootroot00000000000000// // k8s_http.cpp // #ifdef HAS_CAPTURE #include "k8s_http.h" #include "curl/curl.h" #include "curl/easy.h" #include "curl/curlbuild.h" #define BUFFERSIZE 512 // b64 needs this macro #include "b64/encode.h" #include "sinsp.h" #include "sinsp_int.h" #include "k8s.h" #include #include #include #include k8s_http::k8s_http(k8s& k8s, const std::string& component, const std::string& host_and_port, const std::string& protocol, const std::string& credentials, const std::string& api, const std::string& cert): m_curl(curl_easy_init()), m_k8s(k8s), m_protocol(protocol), m_host_and_port(host_and_port), m_api(api), m_component(component), m_credentials(credentials), m_cert(cert), m_watch_socket(0), m_data_ready(false) { if(!m_curl) { throw sinsp_exception("CURL initialization failed."); } curl_version_info_data* data = curl_version_info(CURLVERSION_NOW); if((protocol == "https")) { if(!(data->features | CURL_VERSION_SSL)) { cleanup(); throw sinsp_exception("HTTPS NOT supported"); } } else if((protocol == "http")) { if(!m_cert.empty()) { g_logger.log("Certificate (" + cert + ") provided with HTTP, ignored.", sinsp_logger::SEV_WARNING); } } else { cleanup(); throw sinsp_exception("Protocol not supported:" + protocol); } std::ostringstream url; url << m_protocol << "://"; if(!m_credentials.empty()) { url << m_credentials << '@'; } url << m_host_and_port; url << m_api << '/' << m_component << std::flush; m_url = url.str(); } k8s_http::~k8s_http() { cleanup(); } bool k8s_http::init() { if(!m_curl) { cleanup(); m_curl = curl_easy_init(); } return m_curl != 0; } void k8s_http::cleanup() { if(m_curl) { curl_easy_cleanup(m_curl); m_curl = 0; } } size_t k8s_http::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 k8s_http::get_all_data(std::ostream& os) { CURLcode res = CURLE_OK; g_logger.log(std::string("Retrieving all K8S data from ") + m_url, sinsp_logger::SEV_DEBUG); curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str()); curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1L); if(m_protocol == "https") { if(m_cert.empty()) { check_error(curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER , 0)); } else { g_logger.log(std::string("K8S HTTPS using certificate auth: ") + m_cert, sinsp_logger::SEV_DEBUG); check_error(curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER , 1)); res = curl_easy_setopt(m_curl, CURLOPT_CAINFO, m_cert.c_str()); if(res != CURLE_OK) { os << curl_easy_strerror(res) << std::flush; return false; } } } curl_easy_setopt(m_curl, CURLOPT_NOSIGNAL, 1); //Prevent "longjmp causes uninitialized stack frame" bug curl_easy_setopt(m_curl, CURLOPT_ACCEPT_ENCODING, "deflate"); curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, &k8s_http::write_data); curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &os); 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 long http_code = 0; curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &http_code); if(http_code >= 400) { return false; } } return res == CURLE_OK; } int k8s_http::wait(curl_socket_t sockfd, int for_recv, long timeout_ms) { struct timeval tv; fd_set infd, outfd, errfd; int res; tv.tv_sec = timeout_ms / 1000; tv.tv_usec = (timeout_ms % 1000) * 1000; FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd); FD_SET(sockfd, &errfd); if(for_recv) { FD_SET(sockfd, &infd); } else { FD_SET(sockfd, &outfd); } res = select(sockfd + 1, &infd, &outfd, &errfd, &tv); return res; } int k8s_http::get_watch_socket(long timeout_ms) { if(!m_watch_socket) { long sockextr; size_t iolen; std::string url = m_url; url.insert(m_url.find(m_api) + m_api.size(), "/watch"); check_error(curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str())); check_error(curl_easy_setopt(m_curl, CURLOPT_CONNECT_ONLY, 1L)); check_error(curl_easy_perform(m_curl)); check_error(curl_easy_getinfo(m_curl, CURLINFO_LASTSOCKET, &sockextr)); m_watch_socket = sockextr; if(!wait(m_watch_socket, 0, timeout_ms)) { cleanup(); throw sinsp_exception("Error: timeout."); } std::ostringstream request; request << "GET /api/v1/watch/" << m_component << " HTTP/1.0\r\nHost: " << m_host_and_port << "\r\nConnection: Keep-Alive\r\n"; if(!m_credentials.empty()) { std::istringstream is(m_credentials); std::ostringstream os; base64::encoder().encode(is, os); request << "Authorization: Basic " << os.str() << "\r\n"; } request << "\r\n"; check_error(curl_easy_send(m_curl, request.str().c_str(), request.str().size(), &iolen)); ASSERT (request.str().size() == iolen); if(!wait(m_watch_socket, 1, timeout_ms)) { cleanup(); throw sinsp_exception("Error: timeout."); } g_logger.log(std::string("Collecting data from ") + url, sinsp_logger::SEV_DEBUG); } return m_watch_socket; } bool k8s_http::on_data() { size_t iolen = 0; char buf[1024] = { 0 }; CURLcode ret; do { iolen = 0; try { check_error(ret = curl_easy_recv(m_curl, buf, 1024, &iolen)); } catch(sinsp_exception& ex) { g_logger.log(std::string("Data receive error: ").append(ex.what()), sinsp_logger::SEV_ERROR); return false; } if(iolen > 0) { if(m_data_ready) { m_k8s.on_watch_data(k8s_event_data(k8s_component::get_type(m_component), buf, iolen)); } else // wait for a line with "\r\n" only { std::string data(buf, iolen); std::string end = "\r\n\r\n"; std::string::size_type pos = data.find(end); if(pos != std::string::npos) { pos += end.size(); if(iolen == pos) // right on the edge of data { m_data_ready = true; } else { char* pbuf = &buf[pos]; m_data_ready = true; m_k8s.on_watch_data(k8s_event_data(k8s_component::get_type(m_component), pbuf, iolen - pos)); } } } } else if(ret != CURLE_AGAIN) { g_logger.log("Connection closed", sinsp_logger::SEV_ERROR); m_data_ready = false; return false; } } while(iolen && ret != CURLE_AGAIN); return true; } void k8s_http::on_error(const std::string& err, bool disconnect) { g_logger.log("Socket error:" + err, sinsp_logger::SEV_ERROR); if(disconnect) { cleanup(); } } void k8s_http::check_error(CURLcode res) { if(CURLE_OK != res && CURLE_AGAIN != res) { std::ostringstream os; os << "Error: " << curl_easy_strerror(res); throw sinsp_exception(os.str()); } } #endif // HAS_CAPTURE sysdig-0.8.0/userspace/libsinsp/k8s_http.h000066400000000000000000000023141265472057500205700ustar00rootroot00000000000000// // k8s_http.h // #pragma once #ifdef HAS_CAPTURE #include "curl/curl.h" #include #include #include class k8s; class k8s_http { public: k8s_http(k8s& k8s, const std::string& component, const std::string& host_and_port = "localhost:80", const std::string& protocol = "http", const std::string& credentials = "", const std::string& api = "/api/v1", const std::string& cert = ""); ~k8s_http(); bool get_all_data(std::ostream& os); int get_watch_socket(long timeout_ms); bool is_connected() const; bool on_data(); void on_error(const std::string& err, bool disconnect); private: bool init(); void cleanup(); static size_t write_data(void *ptr, size_t size, size_t nmemb, void *cb); int wait(curl_socket_t sockfd, int for_recv, long timeout_ms); static void check_error(CURLcode res); CURL* m_curl; k8s& m_k8s; std::string m_protocol; std::string m_host_and_port; std::string m_api; std::string m_component; std::string m_credentials; std::string m_cert; std::string m_url; curl_socket_t m_watch_socket; bool m_data_ready; }; inline bool k8s_http::is_connected() const { return m_curl != 0; } #endif // HAS_CAPTURE sysdig-0.8.0/userspace/libsinsp/k8s_net.cpp000066400000000000000000000056201265472057500207350ustar00rootroot00000000000000// // k8s_net.cpp // #ifdef HAS_CAPTURE #include "k8s_net.h" #include "k8s_component.h" #include "k8s.h" #include "sinsp.h" #include "sinsp_int.h" #include #include #include k8s_net::k8s_net(k8s& kube, const std::string& uri, const std::string& api, const std::string& cert) : m_k8s(kube), m_uri(uri + api), m_api(api), m_cert(cert), m_stopped(true), m_collector(kube.watch_in_thread()) #ifndef K8S_DISABLE_THREAD ,m_thread(0) #endif { try { init(); } catch(...) { cleanup(); throw; } } k8s_net::~k8s_net() { end_thread(); cleanup(); } void k8s_net::cleanup() { for (auto& component : k8s_component::list) { delete m_api_interfaces[component.first]; } m_api_interfaces.clear(); } void k8s_net::init() { std::string uri = m_uri.to_string(); std::string::size_type endpos = uri.find_first_of('@'); if(endpos != std::string::npos) { std::string::size_type beginpos = uri.find("://") + 3; if(beginpos != std::string::npos) { m_creds = uri.substr(beginpos, endpos - beginpos); } else { throw sinsp_exception("Bad URI"); } } for (auto& component : k8s_component::list) { m_api_interfaces[component.first] = 0; } } void k8s_net::watch() { bool in_thread = m_k8s.watch_in_thread(); #ifdef K8S_DISABLE_THREAD if(in_thread) { g_logger.log("Thread run requested for non-thread binary.", sinsp_logger::SEV_WARNING); } #else if(m_stopped && in_thread) { subscribe(); m_stopped = false; m_thread = new std::thread(&k8s_collector::get_data, &m_collector); } else #endif // K8S_DISABLE_THREAD if(!in_thread) { if(!m_collector.subscription_count()) { subscribe(); } m_collector.get_data(); } } void k8s_net::subscribe() { for (auto& api : m_api_interfaces) { m_collector.add(api.second); } } void k8s_net::unsubscribe() { m_collector.stop(); m_collector.remove_all(); } void k8s_net::end_thread() { #ifndef K8S_DISABLE_THREAD if(m_thread) { m_thread->join(); delete m_thread; m_thread = 0; } #endif } void k8s_net::stop_watching() { if(!m_stopped) { m_stopped = true; unsubscribe(); end_thread(); } } void k8s_net::get_all_data(const k8s_component::component_map::value_type& component, std::ostream& out) { if(m_api_interfaces[component.first] == 0) { std::string protocol = m_uri.get_scheme(); std::ostringstream os; os << m_uri.get_host(); int port = m_uri.get_port(); if(port) { os << ':' << port; } m_api_interfaces[component.first] = new k8s_http(m_k8s, component.second, os.str(), protocol, m_creds, m_api, m_cert); } if(!m_api_interfaces[component.first]->get_all_data(out)) { std::string err; std::ostringstream* ostr = dynamic_cast(&out); if(ostr) { err = ostr->str(); } throw sinsp_exception(std::string("An error occurred while trying to retrieve data for k8s ") .append(component.second).append(": ").append(err)); } } #endif // HAS_CAPTURE sysdig-0.8.0/userspace/libsinsp/k8s_net.h000066400000000000000000000030101265472057500203710ustar00rootroot00000000000000// // 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_http.h" #include "k8s_collector.h" #include "uri.h" #include #include #ifndef K8S_DISABLE_THREAD #include #endif class k8s; class k8s_net { public: k8s_net(k8s& kube, const std::string& uri = "http://localhost:80", const std::string& api = "/api/v1/", const std::string& cert = ""); ~k8s_net(); void get_all_data(const k8s_component::component_map::value_type& component, std::ostream& out); void watch(); void stop_watching(); bool is_watching() const; bool is_healthy() const; private: void subscribe(); void unsubscribe(); void dispatch_events(); void init(); void end_thread(); bool is_secure(); void cleanup(); typedef std::map api_map_t; k8s& m_k8s; uri m_uri; std::string m_creds; std::string m_api; std::string m_cert; bool m_stopped; api_map_t m_api_interfaces; k8s_collector m_collector; #ifndef K8S_DISABLE_THREAD std::thread* m_thread; #endif }; inline bool k8s_net::is_secure() { return m_uri.get_scheme() == "https"; } inline bool k8s_net::is_watching() const { #ifndef K8S_DISABLE_THREAD return !m_stopped; #else return true; #endif } inline bool k8s_net::is_healthy() const { return m_collector.subscription_count() == static_cast(m_api_interfaces.size()); } #endif // HAS_CAPTURE sysdig-0.8.0/userspace/libsinsp/k8s_state.cpp000066400000000000000000000304141265472057500212660ustar00rootroot00000000000000// // k8s_state.cpp // #include "k8s_state.h" #include "sinsp.h" #include "sinsp_int.h" #include #include // // state // #ifdef K8S_DISABLE_THREAD 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; #endif // K8S_DISABLE_THREAD k8s_state_t::k8s_state_t(bool is_captured) : m_is_captured(is_captured) { } // state/pods void k8s_state_t::update_pod(k8s_pod_t& pod, const Json::Value& item, bool reset) { k8s_pod_t::container_id_list container_ids = k8s_component::extract_pod_container_ids(item); k8s_container::list containers = k8s_component::extract_pod_containers(item); k8s_component::extract_pod_data(item, pod); if(reset) // initially, we just set everything { pod.set_container_ids(std::move(container_ids)); pod.set_containers(std::move(containers)); } else // update call { for(k8s_pod_t::container_id_list::iterator it = container_ids.begin(); it != container_ids.end();) { if(pod.has_container_id(*it)) { // ignoring container ID notification for an existing ID it = container_ids.erase(it); } else { ++it; } } if(container_ids.size()) // what's left are new container IDs { pod.add_container_ids(std::move(container_ids)); } for(k8s_pod_t::container_list::iterator it = containers.begin(); it != containers.end();) { k8s_container* c = pod.get_container(it->get_name()); if(c && (*c != *it)) { *c = *it; it = containers.erase(it); } else { ++it; } } if(containers.size()) // what's left are new containers { pod.add_containers(std::move(containers)); } } } bool k8s_state_t::has_pod(k8s_pod_t& pod) { for(const auto& p : m_pods) { if(p == pod) { return true; } } return false; } // 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; // only controllers and services can have selectors 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_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_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_SERVICES: return get_component(m_services, 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_SERVICES: m_services.clear(); break; case k8s_component::K8S_COMPONENT_COUNT: default: break; } } } // state/caching void k8s_state_t::update_cache(const k8s_component::component_map::key_type& component) { #ifdef K8S_DISABLE_THREAD 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_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; default: return; } #endif // K8S_DISABLE_THREAD } 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 == "Service") { return k8s_component::K8S_SERVICES; } throw sinsp_exception("Unknown component kind:" + comp); } Json::Value k8s_state_t::extract_capture_data(const Json::Value& item) { k8s_component::type component = component_from_json(item); Json::Value cap_item; #ifdef HAS_CAPTURE 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_cid; new_cid["containerID"] = c_status["containerID"]; cap_status["containerStatuses"].append(new_cid); } } } 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; 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.8.0/userspace/libsinsp/k8s_state.h000066400000000000000000000244301265472057500207340ustar00rootroot00000000000000// // 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; k8s_state_t(bool is_captured = false); // // 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 reset); 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); // // 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); // // 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& container, const std::string& uid) const { for (auto& comp : container) { if(uid == comp.get_uid()) { return true; } } return false; } // Returns a pointer to existing component, if it exists. // If component does not exist, it returns null pointer. template T* get_component(C& container, const std::string& uid) { for (auto& comp : container) { if(comp.get_uid() == uid) { return ∁ } } return 0; } // 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; } } container.emplace_back(std::move(T(name, uid, ns))); return container.back(); } 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); return true; } } return false; } void clear(k8s_component::type type = k8s_component::K8S_COMPONENT_COUNT); #ifdef K8S_DISABLE_THREAD // // cached lookup support // // 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; } #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) { if(m_is_captured) { m_capture_events.emplace_back(Json::FastWriter().write(extract_capture_data(item))); } } std::string dequeue_capture_event() { if(!m_capture_events.size()) { throw sinsp_exception("Invalid event dequeue request."); } std::string ev = std::move(m_capture_events.front()); m_capture_events.pop_front(); return ev; } #endif // HAS_CAPTURE #endif // K8S_DISABLE_THREAD private: void update_cache(const k8s_component::component_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); #ifdef K8S_DISABLE_THREAD 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) { 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); } 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); } } 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; } 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) namespace_map m_namespace_map; container_pod_map m_container_pods; pod_service_map m_pod_services; pod_rc_map m_pod_rcs; #ifdef HAS_CAPTURE event_list_t m_capture_events; #endif // HAS_CAPTURE #endif // K8S_DISABLE_THREAD k8s_namespaces m_namespaces; k8s_nodes m_nodes; k8s_pods m_pods; k8s_controllers m_controllers; k8s_services m_services; bool m_is_captured; friend class k8s_dispatcher; 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)); } // 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)); } // general inline void k8s_state_t::set_last_pod_node_name(const std::string& name) { m_pods.back().set_node_name(name); } inline void k8s_state_t::set_last_pod_host_ip(const std::string& host_ip) { m_pods.back().set_host_ip(host_ip); } inline void k8s_state_t::set_last_pod_internal_ip(const std::string& internal_ip) { m_pods.back().set_internal_ip(internal_ip); } inline void k8s_state_t::add_last_node_ip(std::string&& ip) { m_nodes.back().emplace_host_ip(std::move(ip)); } inline void k8s_state_t::add_last_pod_container_id(std::string&& container_id) { m_pods.back().emplace_container_id(std::move(container_id)); } sysdig-0.8.0/userspace/libsinsp/logger.cpp000066400000000000000000000072401265472057500206410ustar00rootroot00000000000000/* 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("sisnsp.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 wrirting"); } 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::set_severity(severity sev) { if(m_sev > SEV_MAX) { throw sinsp_exception("invalid log severity"); } m_sev = sev; } void sinsp_logger::log(string msg, severity sev) { struct timeval ts; if(sev < m_sev) { return; } if((m_flags & sinsp_logger::OT_NOTS) == 0) { 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 %s", 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.c_str()); } else { snprintf(m_tbuf, sizeof(m_tbuf), "%s", msg.c_str()); } if(m_flags & sinsp_logger::OT_CALLBACK) { (*m_callback)(m_tbuf, (uint32_t)sev); } else if(m_flags & sinsp_logger::OT_FILE) { fprintf(m_file, "%s\n", m_tbuf); fflush(m_file); } else if(m_flags & sinsp_logger::OT_STDOUT) { fprintf(stdout, "%s\n", m_tbuf); fflush(stdout); } else if(m_flags & sinsp_logger::OT_STDERR) { fprintf(stderr, "%s\n", m_tbuf); fflush(stderr); } } char* sinsp_logger::format(severity sev, 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); 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.8.0/userspace/libsinsp/logger.h000066400000000000000000000034271265472057500203110ustar00rootroot00000000000000/* 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)(char* str, uint32_t sev); class SINSP_PUBLIC sinsp_logger { public: enum severity { SEV_DEBUG = 0, SEV_INFO = 1, SEV_WARNING = 2, SEV_ERROR = 3, SEV_CRITICAL = 4, SEV_MAX = SEV_CRITICAL, }; 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 set_severity(severity sev); void log(string msg, severity sev=SEV_INFO); // Log functions that accepts printf syntax and return the formatted buffer. char* format(severity sev, const char* fmt, ...); char* format(const char* fmt, ...); private: FILE* m_file; sinsp_logger_callback m_callback; uint32_t m_flags; severity m_sev; char m_tbuf[32768]; }; sysdig-0.8.0/userspace/libsinsp/marathon_component.cpp000066400000000000000000000126671265472057500232660ustar00rootroot00000000000000// // marathon_component.cpp // #include "marathon_component.h" #include "sinsp.h" #include "sinsp_int.h" #include #include // // component // 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_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()); } // // 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); } 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); return true; } } g_logger.log("Task [" + task_id + "] not found in app [" + get_id() + ']', sinsp_logger::SEV_WARNING); 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; } // // group // marathon_group::marathon_group(const std::string& id) : marathon_component(marathon_component::MARATHON_GROUP, id) { } marathon_group::marathon_group(const marathon_group& other): marathon_component(other), std::enable_shared_from_this() { } marathon_group::marathon_group(marathon_group&& other): marathon_component(std::move(other)) { } marathon_group& marathon_group::operator=(const marathon_group& other) { marathon_component::operator =(other); return *this; } marathon_group& marathon_group::operator=(const marathon_group&& other) { marathon_component::operator =(std::move(other)); return *this; } 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; } void marathon_group::print() const { std::cout << get_id() << std::endl; for(auto& group : m_groups) { group.second->print(); } } sysdig-0.8.0/userspace/libsinsp/marathon_component.h000066400000000000000000000100471265472057500227210ustar00rootroot00000000000000// // 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; // // 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& name); static std::string get_name(type t); static type get_type(const std::string& name); private: type m_type; std::string m_id; }; class marathon_app; // // 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::map> group_map_t; marathon_group(const std::string& id); marathon_group(const marathon_group& other); marathon_group(marathon_group&& other); marathon_group& operator=(const marathon_group& other); marathon_group& operator=(const marathon_group&& other); app_ptr_t get_app(const std::string& id); 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); bool remove(const std::string& id); void print() const; 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; }; // // app // class marathon_app : public marathon_component { 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); const task_list_t& get_tasks() const; std::string get_group_id() const; static std::string get_group_id(const std::string& app_id); void clear_cache(); private: task_list_t m_tasks; 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; } // // 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); } // // app // inline const marathon_app::task_list_t& marathon_app::get_tasks() const { return m_tasks; } sysdig-0.8.0/userspace/libsinsp/marathon_dispatcher.cpp000066400000000000000000000172031265472057500234010ustar00rootroot00000000000000// // marathon_dispatcher.cpp // #include "marathon_dispatcher.h" #include "mesos_event_data.h" #include "sinsp.h" #include "sinsp_int.h" #include "utils.h" #include #include #include #include #include marathon_dispatcher::marathon_dispatcher(mesos_state_t& state, const std::string& framework_id): m_state(state), m_framework_id(framework_id) { g_logger.log("Created marathon_dispatcher for framework: " + framework_id, sinsp_logger::SEV_DEBUG); } void marathon_dispatcher::enqueue(mesos_event_data&& event_data) { m_messages.emplace_back(event_data.get_data()); dispatch(); } void marathon_dispatcher::dispatch() { for (list::iterator it = m_messages.begin(); it != m_messages.end();) { extract_data(*it); it = m_messages.erase(it); } } void marathon_dispatcher::extract_data(const std::string& json) { Json::Value root; Json::Reader reader; if(reader.parse(json, root, false)) { switch(mesos_event_data::get_event_type(root)) { case mesos_event_data::MESOS_API_POST_EVENT: //handle_api_post(root); //break; case mesos_event_data::MESOS_STATUS_UPDATE_EVENT: //handle_status_update(root); //break; case mesos_event_data::MESOS_APP_TERMINATED_EVENT: //handle_app_terminate(root); //break; case mesos_event_data::MESOS_GROUP_CHANGE_SUCCESS_EVENT: //handle_group_change(root); //break; case mesos_event_data::MESOS_DEPLOYMENT_SUCCESS_EVENT: //handle_deployment_success(root); m_state.set_marathon_changed(true); break; case mesos_event_data::MESOS_IGNORED_EVENT: break; default: { std::string evt; Json::Value ev_type = root["eventType"]; if(!ev_type.isNull() && ev_type.isString()) { evt = ev_type.asString(); } g_logger.log("marathon_dispatcher::extract_data: Unknown event " + evt, sinsp_logger::SEV_WARNING); } } } } void marathon_dispatcher::handle_status_update(const Json::Value& root) { std::string slave_id = get_json_string(root, "slaveId"); std::string app_id = get_json_string(root, "appId"); if(!slave_id.empty() && !app_id.empty()) { std::string task_status = get_json_string(root, "taskStatus"); if(!task_status.empty()) { std::string task_id = get_json_string(root, "taskId"); if(!task_id.empty()) { g_logger.log("App [" + app_id + "], task [" + task_id + "] changed status to " + task_status + " on slave [" + slave_id + "].\nVersion: " + get_json_string(root, "version") + ", Timestamp: " + get_json_string(root, "version"), sinsp_logger::SEV_INFO); if(task_status == "TASK_RUNNING") { std::string task_name; std::string::size_type pos = task_id.rfind('.'); if(pos != std::string::npos && pos > 0) { task_name = task_id.substr(0, pos); } g_logger.log("Handling running notification for task " + task_name + " [" + task_id + ']', sinsp_logger::SEV_INFO); std::string group_id = marathon_app::get_group_id(app_id); if(!group_id.empty() && m_state.get_group(group_id)) { mesos_task::ptr_t t = mesos_task::make_task(root); if(t) { if(m_state.add_or_replace_app(app_id, group_id, task_id)) { g_logger.log("Added or replaced app: " + app_id + ", group: " + group_id + ", task ID:" + task_id + ']', sinsp_logger::SEV_DEBUG); t->set_marathon_app_id(app_id); m_state.add_or_replace_task(m_state.get_framework(m_framework_id), t); } else { g_logger.log("Error adding or updating task [" + task_id + ']', sinsp_logger::SEV_ERROR); return; } } else { g_logger.log("Error creating task " + task_name + " [" + task_id + ']', sinsp_logger::SEV_ERROR); } } else { g_logger.log("Non-existent group ID (" + group_id + "] for task [" + task_id + ']', sinsp_logger::SEV_ERROR); return; } } else if(task_status == "TASK_FINISHED" || // TERMINAL. The task finished successfully. task_status == "TASK_FAILED" || // TERMINAL. The task failed to finish successfully. task_status == "TASK_KILLED" || // TERMINAL. The task was killed by the executor. task_status == "TASK_LOST" || // TERMINAL. The task failed but can be rescheduled. task_status == "TASK_ERROR") // TERMINAL. The task description contains an error. { std::string msg = get_json_string(root, "message"); std::ostringstream os; os << "Handling removal notification for task [" << task_id << ']'; if(!msg.empty()) { os << ", termination message: " << msg; } g_logger.log(os.str(), sinsp_logger::SEV_INFO); try { m_state.remove_task(m_state.get_framework(m_framework_id), task_id); } catch(std::exception& ex) { g_logger.log(ex.what(), sinsp_logger::SEV_ERROR); return; } g_logger.log("Succesfully removed task [" + task_id + ']', sinsp_logger::SEV_INFO); } else { // Ignored: // TASK_STAGING; // Initial state. Framework status updates should not use. // TASK_STARTING; g_logger.log("Slave [" + slave_id + "], task " + get_json_string(root, "appId") + " (" + task_id + ") ignored changed status to " + task_status, sinsp_logger::SEV_DEBUG); } } } } } void marathon_dispatcher::handle_api_post(const Json::Value& root) { g_logger.log("MESOS_API_POST_EVENT", sinsp_logger::SEV_DEBUG); std::string uri = get_json_string(root, "uri"); if(uri == "/v2/apps") { Json::Value app_obj = root["appDefinition"]; if(!app_obj.empty()) { std::string app_id = get_json_string(app_obj, "id"); g_logger.log("Adding app [" + app_id + ']', sinsp_logger::SEV_INFO); std::string group_id = marathon_app::get_group_id(app_id); if(!group_id.empty()) { m_state.add_or_replace_app(app_id, group_id); } else { g_logger.log("error adding app [" + app_id + ']', sinsp_logger::SEV_ERROR); } } } } void marathon_dispatcher::handle_app_terminate(const Json::Value& root) { g_logger.log("MESOS_APP_TERMINATED_EVENT", sinsp_logger::SEV_DEBUG); std::string id = get_json_string(root, "appId"); g_logger.log("Removing app [" + id + ']', sinsp_logger::SEV_INFO); if(!m_state.remove_app(id)) { g_logger.log("App [" + id + "] not found.", sinsp_logger::SEV_ERROR); return; } g_logger.log("Succesfully removed app [" + id + ']', sinsp_logger::SEV_INFO); } void marathon_dispatcher::handle_group_change(const Json::Value& root) { g_logger.log("MESOS_GROUP_CHANGE_SUCCESS_EVENT", sinsp_logger::SEV_DEBUG); //g_logger.log(root.toStyledString(), sinsp_logger::SEV_DEBUG); Json::Value group_id = root["id"]; if(!group_id.isNull() && group_id.isString()) { std::string id = group_id.asString(); g_logger.log("Handling group [" + id + ']', sinsp_logger::SEV_INFO); std::string::size_type pos = id.rfind('/'); if(pos != std::string::npos) { std::string parent_id; marathon_group::ptr_t parent = 0; if(pos == 0 && id.size() > 1) { parent = m_state.get_group("/"); } else if(pos != 0) { parent_id = id.substr(0, pos); parent = m_state.get_group(parent_id); } if(m_state.handle_groups(root, m_state.add_group(root, parent, m_framework_id), m_framework_id)) { g_logger.log("Sucesfully handled notification for group [" + id + ']', sinsp_logger::SEV_INFO); return; } } g_logger.log("An error occurred while handling group [" + id + ']', sinsp_logger::SEV_ERROR); } g_logger.log("An error occurred while handling group (no ID found)", sinsp_logger::SEV_ERROR); } void marathon_dispatcher::handle_deployment_success(const Json::Value& /*root*/) { g_logger.log("MESOS_DEPLOYMENT_SUCCESS_EVENT", sinsp_logger::SEV_DEBUG); } sysdig-0.8.0/userspace/libsinsp/marathon_dispatcher.h000066400000000000000000000027521265472057500230510ustar00rootroot00000000000000// // marathon_dispatcher.h // // kubernetes REST API notification abstraction // #pragma once #include "mesos_common.h" #include "mesos_state.h" #include "mesos_event_data.h" #include "json/json.h" #include #include class marathon_dispatcher { public: typedef std::shared_ptr ptr_t; struct msg_data { mesos_event_data::type m_type = mesos_event_data::MESOS_UNKNOWN_EVENT; std::string m_status; bool is_valid() const { return m_type != mesos_event_data::MESOS_UNKNOWN_EVENT; } }; marathon_dispatcher() = delete; marathon_dispatcher(mesos_state_t& state, const std::string& framework_id); void enqueue(mesos_event_data&& data); void extract_data(const std::string& json); const std::string& get_id() const; private: const std::string& next_msg(); void remove(); void dispatch(); void handle_api_post(const Json::Value& root); void handle_status_update(const Json::Value& root); void handle_app_terminate(const Json::Value& root); void handle_group_change(const Json::Value& root); void handle_deployment_success(const Json::Value& root); typedef std::deque list; list m_messages; mesos_state_t& m_state; std::string m_framework_id; }; inline const std::string& marathon_dispatcher::next_msg() { return m_messages.front(); } inline void marathon_dispatcher::remove() { m_messages.pop_front(); } inline const std::string& marathon_dispatcher::get_id() const { return m_framework_id; } sysdig-0.8.0/userspace/libsinsp/marathon_http.cpp000066400000000000000000000072071265472057500222350ustar00rootroot00000000000000// // marathon_http.cpp // #ifdef HAS_CAPTURE #include "marathon_http.h" #include "curl/curl.h" #include "curl/easy.h" #include "curl/curlbuild.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): mesos_http(m, url) { g_logger.log("Creating Marathon HTTP object for [" + url.to_string() + "] ...", sinsp_logger::SEV_DEBUG); if(refresh_data()) { g_logger.log("Created Marathon HTTP connection (" + url.to_string() + ") 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; } /*TODO: see comment in mesos.cpp constructor bool marathon_http::on_data() { size_t iolen = 0; char buf[1024] = { 0 }; CURLcode ret; do { iolen = 0; try { check_error(ret = curl_easy_recv(get_curl(), buf, 1024, &iolen)); } catch(sinsp_exception& ex) { g_logger.log(std::string("Data receive error: ").append(ex.what()), sinsp_logger::SEV_ERROR); return false; } if(iolen > 0) { m_data.append(buf, iolen); } else if(ret != CURLE_AGAIN) { g_logger.log("Connection closed", sinsp_logger::SEV_ERROR); return false; } } while(iolen && ret != CURLE_AGAIN); const std::string end = "\r\n\r\n"; std::string::size_type pos = m_data.find(end); while(!m_data.empty() && pos != std::string::npos) { std::string msg = m_data.substr(0, pos); trim(msg); if(msg.size() && msg.find("event:") != std::string::npos) { try { if(!mesos_event_data::is_ignored(mesos_event_data::get_event_type(msg))) { get_mesos().on_watch_data(get_framework_id(), mesos_event_data(msg)); } } catch(std::exception& ex) { g_logger.log(ex.what(), sinsp_logger::SEV_ERROR); return false; } } m_data = m_data.substr(pos + end.size()); pos = m_data.find(end); } 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(); } /* void marathon_http::on_error(const std::string& err, bool disconnect) { g_logger.log("Socket error:" + err, sinsp_logger::SEV_ERROR); if(disconnect) { cleanup(); } } */ #endif // HAS_CAPTURE sysdig-0.8.0/userspace/libsinsp/marathon_http.h000066400000000000000000000010071265472057500216720ustar00rootroot00000000000000// // 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); ~marathon_http(); bool refresh_data(); std::string get_groups(const std::string& group_id); private: //bool on_data(); //void on_error(const std::string& err, bool disconnect); std::string m_data; }; #endif // HAS_CAPTURE sysdig-0.8.0/userspace/libsinsp/memmem.cpp000066400000000000000000000021531265472057500206350ustar00rootroot00000000000000/* 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.8.0/userspace/libsinsp/mesos.cpp000066400000000000000000000303171265472057500205110ustar00rootroot00000000000000// // mesos.cpp // #ifndef _WIN32 #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"; 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"; mesos::mesos(const std::string& state_uri, const std::string& state_api, const uri_list_t& marathon_uris, const std::string& groups_api, const std::string& apps_api, const std::string& /*watch_api*/): #ifdef HAS_CAPTURE m_collector(false), #endif // HAS_CAPTURE m_creation_logged(false) { #ifdef HAS_CAPTURE m_state_http = std::make_shared(*this, state_uri + state_api); rebuild_mesos_state(true); for(const auto& muri : marathon_uris) { int port = (muri.substr(0, 5) == "https") ? 443 : 80; std::string::size_type pos = muri.rfind(':'); if(pos != std::string::npos) { std::string::size_type ppos = muri.find('/', pos); if(ppos == std::string::npos) { ppos = pos + (muri.length() - pos); } ASSERT(ppos - (pos + 1) > 0); port = std::stoi(muri.substr(pos + 1, ppos - (pos + 1))); } m_marathon_groups_http[port] = std::make_shared(*this, muri + groups_api); m_marathon_apps_http[port] = std::make_shared(*this, muri + apps_api); /* TODO: enable marathon state rebuild based on marathon change events; currently, the problem is the design of http_mesos/http_marathon class hierarchy - there is a virtual on_data() member, which can be used by either events or non-blocking full state polling, but not for both uri url(muri + watch_api); host_and_port = url.get_host(); port = url.get_port(); if(port) { host_and_port.append(1, ':').append(std::to_string(port)); } request.str(""); request << "GET " << url.get_path() << " HTTP/1.1\r\nHost: " << host_and_port << "\r\nAccept: text/event-stream\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"; } request << "\r\n"; m_marathon_watch_http[port] = std::make_shared(*this, muri + watch_api, request.str(), true); m_collector.add(m_marathon_watch_http[port]); m_dispatch[port] = std::make_shared(m_state, m_marathon_watch_http[port]->get_id()); */ } if(has_marathon()) { rebuild_marathon_state(true); } #endif // HAS_CAPTURE } mesos::~mesos() { } void mesos::refresh() { rebuild_mesos_state(); if(has_marathon()) { //TODO: optimize - rebuild only if there was marathon change //watch_marathon(); //if(m_state.get_marathon_changed()) { rebuild_marathon_state(); } } } void mesos::rebuild_mesos_state(bool full) { #ifdef HAS_CAPTURE if(full) { clear_mesos(); m_state_http->get_all_data(&mesos::parse_state); } 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) { group_http.second->get_all_data(&mesos::parse_groups); } for(auto& app_http : m_marathon_apps_http) { app_http.second->get_all_data(&mesos::parse_apps); } } else { connect_marathon(); send_marathon_data_request(); collect_data(); } m_state.set_marathon_changed(false); } #endif // HAS_CAPTURE } #ifdef HAS_CAPTURE void mesos::send_marathon_data_request() { if(has_marathon()) { for(auto& group_http : m_marathon_groups_http) { group_http.second->send_request(); } for(auto& app_http : m_marathon_apps_http) { app_http.second->send_request(); } } } void mesos::connect_marathon() { if(has_marathon()) { for(auto& group_http : m_marathon_groups_http) { connect(group_http.second, &mesos::set_marathon_groups_json); } for(auto& app_http : m_marathon_apps_http) { connect(app_http.second, &mesos::set_marathon_apps_json); } } } void mesos::send_mesos_data_request() { m_state_http->send_request(); } void mesos::connect_mesos() { connect(m_state_http, &mesos::set_state_json); } #endif // HAS_CAPTURE bool mesos::is_alive() const { #ifdef HAS_CAPTURE if(!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->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->is_connected()) { g_logger.log("Marathon apps connection loss.", sinsp_logger::SEV_WARNING); return false; } } #endif // HAS_CAPTURE return true; } void mesos::watch_marathon() { #ifdef HAS_CAPTURE if(has_marathon()) { if(m_marathon_watch_http.size()) { if(!m_collector.subscription_count()) { for(auto watch_http : m_marathon_watch_http) { m_collector.add(watch_http.second); } } m_collector.get_data(); } } else { throw sinsp_exception("Attempt to watch non-existing Marathon framework."); } #endif // HAS_CAPTURE } void mesos::add_task_labels(std::string& json) { #ifdef HAS_CAPTURE Json::Value root; Json::Reader reader; try { if(reader.parse(json, root, false)) { if(mesos_event_data::get_event_type(root) == mesos_event_data::MESOS_STATUS_UPDATE_EVENT) { if(!root["taskId"].isNull()) { if(!root["taskStatus"].isNull() && root["taskStatus"].isString() && root["taskStatus"].asString() == "TASK_RUNNING") { Json::Value labels = m_state_http->get_task_labels(root["taskId"].asString()); if(!labels.isNull() && labels.isArray()) { root["labels"] = labels; json = Json::FastWriter().write(root); } } } } } else { g_logger.log("Error parsing task update message.\nJSON:\n---\n" + json + "\n---", sinsp_logger::SEV_ERROR); } } catch(std::exception& ex) { g_logger.log(std::string("Error while looking for taks labels:") + ex.what(), sinsp_logger::SEV_ERROR); } #endif // HAS_CAPTURE } #ifdef HAS_CAPTURE void mesos::get_groups(marathon_http::ptr_t http, std::string& json) { std::string group_ev_type = mesos_event_data::m_events[mesos_event_data::MESOS_GROUP_CHANGE_SUCCESS_EVENT]; Json::Value root; Json::Reader reader; try { if(reader.parse(json, root, false)) { Json::Value event_type = root["eventType"]; if(!event_type.isNull() && event_type.isString() && event_type.asString() == group_ev_type) { Json::Value group_id = root["groupId"]; if(!group_id.isNull() && group_id.isString()) { std::string gid = group_id.asString(); if(!gid.empty()) { json = http->get_groups(gid); if(reader.parse(json, root, false)) { root["eventType"] = group_ev_type; json = Json::FastWriter().write(root); } } } } } else { g_logger.log("Error parsing task update message.\nJSON:\n---\n" + json + "\n---", sinsp_logger::SEV_ERROR); } } catch(std::exception& ex) { g_logger.log(std::string("Error while looking for taks labels:") + ex.what(), sinsp_logger::SEV_ERROR); } } void mesos::on_watch_data(const std::string& framework_id, mesos_event_data&& msg) { for(auto& dispatcher : m_dispatch) { if(framework_id == dispatcher.second->get_id()) { dispatcher.second->enqueue(std::move(msg)); break; } } } #endif // HAS_CAPTURE void mesos::handle_frameworks(const Json::Value& root) { Json::Value frameworks = root["frameworks"]; if(!frameworks.isNull()) { for(const auto& framework : frameworks) { add_framework(framework); } } else { g_logger.log("No frameworks found.", sinsp_logger::SEV_WARNING); } } void mesos::handle_slaves(const Json::Value& root) { 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; Json::Value fname = framework["name"]; 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::add_slave(const Json::Value& slave) { std::string name, uid; Json::Value sname = slave["hostname"]; 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) { 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) { Json::Value tasks = f_val["tasks"]; add_tasks_impl(framework, tasks); } void mesos::set_state_json(std::string&& json, const std::string&) { if(!json.empty()) { //g_logger.log("Received state JSON " + std::to_string(json.size()), sinsp_logger::SEV_DEBUG); m_mesos_state_json = std::move(json); } else { g_logger.log("Received empty state JSON", sinsp_logger::SEV_WARNING); } } void mesos::parse_state(std::string&& json, const std::string&) { Json::Value root; Json::Reader reader; if(reader.parse(json, root, false)) { clear_mesos(); handle_frameworks(root); handle_slaves(root); if(!m_creation_logged) { m_creation_logged = true; } } else { throw sinsp_exception("Invalid JSON (parsing Mesos state failed)."); } } void mesos::set_marathon_groups_json(std::string&& json, const std::string& framework_id) { if(!json.empty()) { m_marathon_groups_json[framework_id] = std::move(json); } else { g_logger.log("Received empty groups JSON", sinsp_logger::SEV_WARNING); } } void mesos::parse_groups(std::string&& json, const std::string& framework_id) { m_state.parse_groups(std::move(json), framework_id); } void mesos::set_marathon_apps_json(std::string&& json, const std::string& framework_id) { if(!json.empty()) { g_logger.log("Received apps JSON (" + std::to_string(json.size()) + " bytes) for framework [" + framework_id + ']', sinsp_logger::SEV_DEBUG); m_marathon_apps_json[framework_id] = std::move(json); } else { g_logger.log("Received empty apps JSON", sinsp_logger::SEV_WARNING); } } void mesos::parse_apps(std::string&& json, const std::string& framework_id) { m_state.parse_apps(std::move(json), framework_id); } #endif // _WIN32 sysdig-0.8.0/userspace/libsinsp/mesos.h000066400000000000000000000125701265472057500201570ustar00rootroot00000000000000// // mesos.h // #ifndef _WIN32 #pragma once #include "json/json.h" #include "mesos_common.h" #include "mesos_component.h" #include "mesos_http.h" #include "marathon_http.h" #include "mesos_state.h" #include "mesos_event_data.h" #include "marathon_dispatcher.h" #include "mesos_collector.h" #include "uri.h" #include #include #include class mesos { public: 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; typedef std::vector uri_list_t; mesos(const std::string& state_uri = default_state_uri, const std::string& state_api = default_state_api, const uri_list_t& marathon_uris = uri_list_t(), const std::string& groups_api = "", const std::string& apps_api = "", const std::string& watch_api = ""); ~mesos(); const mesos_state_t& get_state() const; bool is_alive() const; void refresh(); void clear_mesos(); bool has_marathon() const; void watch_marathon(); void clear_marathon(); #ifdef HAS_CAPTURE void send_data_request(bool collect = true) { connect_mesos(); send_mesos_data_request(); if(has_marathon()) { connect_marathon(); send_marathon_data_request(); } if(!m_mesos_state_json.empty()) { return; } for(auto& group : m_marathon_groups_json) { if(!group.second.empty()) { return; } } for(auto& app : m_marathon_apps_json) { if(!app.second.empty()) { return; } } if(collect) { collect_data(); } } void collect_data() { m_collector.get_data(); if(!m_mesos_state_json.empty()) { if(!m_marathon_apps_json.empty() && !m_marathon_groups_json.empty()) { for(auto& group : m_marathon_groups_json) { if(group.second.size()) { 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.empty()) { if(!m_mesos_state_json.empty()) { parse_state(std::move(m_mesos_state_json), ""); m_mesos_state_json.clear(); } // +++ 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); // --- group.second.clear(); app_it->second.clear(); } } 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)."); } } } } } } #endif // HAS_CAPTURE private: #ifdef HAS_CAPTURE void send_mesos_data_request(); void connect_mesos(); void send_marathon_data_request(); void connect_marathon(); template void connect(T http, typename T::element_type::callback_func_t func) { 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); } } #endif // HAS_CAPTURE 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 set_state_json(std::string&& json, const std::string&); void parse_state(std::string&& json, const std::string&); void set_marathon_groups_json(std::string&& json, const std::string& framework_id); void parse_groups(std::string&& json, const std::string& framework_id); void set_marathon_apps_json(std::string&& json, const std::string& framework_id); void parse_apps(std::string&& json, const std::string& framework_id); void add_task_labels(std::string& json); #ifdef HAS_CAPTURE void on_watch_data(const std::string& framework_id, mesos_event_data&& msg); void get_groups(marathon_http::ptr_t http, std::string& json); typedef std::unordered_map marathon_http_map; typedef std::unordered_map marathon_disp_map; mesos_http::ptr_t m_state_http; marathon_http_map m_marathon_groups_http; marathon_http_map m_marathon_apps_http; marathon_http_map m_marathon_watch_http; mesos_collector m_collector; marathon_disp_map m_dispatch; #endif // HAS_CAPTURE mesos_state_t m_state; bool m_creation_logged; typedef std::map json_map_type_t; std::string m_mesos_state_json; json_map_type_t m_marathon_groups_json; json_map_type_t m_marathon_apps_json; 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; } #ifdef HAS_CAPTURE inline bool mesos::has_marathon() const { return m_marathon_groups_http.size() || m_marathon_apps_http.size(); } #endif // HAS_CAPTURE inline void mesos::clear_mesos() { m_state.clear_mesos(); } inline void mesos::clear_marathon() { m_state.clear_marathon(); } #endif // _WIN32 sysdig-0.8.0/userspace/libsinsp/mesos_collector.cpp000066400000000000000000000062651265472057500225640ustar00rootroot00000000000000// // 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; } void 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; } } } void mesos_collector::remove_all() { clear(); for(socket_map_t::iterator it = m_sockets.begin(); it != m_sockets.end();) { remove(it++); } } bool mesos_collector::is_active() const { return subscription_count() > 0; } 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()) { res = select(m_nfds + 1, &m_infd, NULL, &m_errfd, &tv); if(res < 0) // error { std::string err = strerror(errno); g_logger.log(err, sinsp_logger::SEV_ERROR); remove_all(); } else // data or idle { for(auto& sock : m_sockets) { if(FD_ISSET(sock.first, &m_infd)) { if(!sock.second->on_data()) { if(errno != EAGAIN) { remove(m_sockets.find(sock.first)); } } } else { FD_SET(sock.first, &m_infd); } if(FD_ISSET(sock.first, &m_errfd)) { if(errno != EAGAIN) { std::string err = strerror(errno); g_logger.log(err, sinsp_logger::SEV_ERROR); sock.second->on_error(err, true); remove(m_sockets.find(sock.first)); } } else { FD_SET(sock.first, &m_errfd); } } } } else { g_logger.log("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("Collector error: ") + ex.what(), sinsp_logger::SEV_ERROR); remove_all(); m_stopped = true; } } #endif // HAS_CAPTURE sysdig-0.8.0/userspace/libsinsp/mesos_collector.h000066400000000000000000000016511265472057500222230ustar00rootroot00000000000000// // 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 has(std::shared_ptr handler); bool remove(std::shared_ptr handler); private: void clear(); void 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.8.0/userspace/libsinsp/mesos_common.h000066400000000000000000000000501265472057500215150ustar00rootroot00000000000000// // mesos_common.h // #pragma once sysdig-0.8.0/userspace/libsinsp/mesos_component.cpp000066400000000000000000000135631265472057500225770ustar00rootroot00000000000000// // 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 // mesos_framework::mesos_framework(const std::string& name, const std::string& uid) : mesos_component(mesos_component::MESOS_FRAMEWORK, name, uid) { } mesos_framework::~mesos_framework() { } 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; } 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.8.0/userspace/libsinsp/mesos_component.h000066400000000000000000000114421265472057500222360ustar00rootroot00000000000000// // 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 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: 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(); 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; } // // task // sysdig-0.8.0/userspace/libsinsp/mesos_event_data.cpp000066400000000000000000000055401265472057500227030ustar00rootroot00000000000000// // mesos_event_data.cpp // #include "mesos_event_data.h" mesos_event_data::event_map_t mesos_event_data::m_events = { { MESOS_UNKNOWN_EVENT, "unknown_event" }, { MESOS_API_POST_EVENT, "api_post_event" }, { MESOS_STATUS_UPDATE_EVENT, "status_update_event" }, { MESOS_APP_TERMINATED_EVENT, "app_terminated_event" }, { MESOS_DEPLOYMENT_SUCCESS_EVENT, "deployment_success" }, { MESOS_GROUP_CHANGE_SUCCESS_EVENT, "group_change_success" }, { MESOS_IGNORED_EVENT, "ignored_event" } }; mesos_event_data::mesos_event_data(const std::string& data): m_event(MESOS_UNKNOWN_EVENT) { std::string event_type = get_event_type(data); if(!event_type.empty()) { m_event = get_event_type_from_name(event_type); std::string name = "data:"; std::string::size_type pos = data.find(name); if(pos != std::string::npos) { pos += name.size(); std::string val = data.substr(pos); m_data = trim(val); //g_logger.log("Mesos event data:" + m_data, sinsp_logger::SEV_DEBUG); } else { throw sinsp_exception("Invalid event data."); } } else { g_logger.log("Event data object created for ignored event: " + event_type, sinsp_logger::SEV_WARNING); } } mesos_event_data::mesos_event_data(const mesos_event_data& other): m_event(other.m_event), m_data(other.m_data) { } mesos_event_data::mesos_event_data(mesos_event_data&& other): m_event(std::move(other.m_event)), m_data(std::move(other.m_data)) { } mesos_event_data& mesos_event_data::operator=(mesos_event_data&& other) { if(this != &other) { m_event = other.m_event; m_data = other.m_data; } return *this; } mesos_event_data::type mesos_event_data::get_event_type_from_name(const std::string& name) { if(!name.empty()) { for(const auto& evt : m_events) { if(name == evt.second) { return evt.first; } } return MESOS_IGNORED_EVENT; } return MESOS_UNKNOWN_EVENT; } std::string mesos_event_data::get_event_type(const std::string& data) { std::string event_type; std::string name = "event:"; std::string::size_type pos = data.find(name); std::string::size_type end = data.find('\n', pos); if(pos != std::string::npos && end != std::string::npos) { pos += name.size(); if(end - pos > 0) { event_type = data.substr(pos, end - pos); trim(event_type); for(const auto& evt : m_events) { if(event_type == evt.second) { return event_type; } } return m_events[MESOS_IGNORED_EVENT]; } } return m_events[MESOS_UNKNOWN_EVENT]; } mesos_event_data::type mesos_event_data::get_event_type(const Json::Value& root) { Json::Value ev_type = root["eventType"]; if(!ev_type.isNull() && ev_type.isString()) { for(const auto& evt : m_events) { if(evt.second == ev_type.asString()) { return evt.first; } } return MESOS_IGNORED_EVENT; } return MESOS_UNKNOWN_EVENT; } sysdig-0.8.0/userspace/libsinsp/mesos_event_data.h000066400000000000000000000027041265472057500223470ustar00rootroot00000000000000// // mesos_event_data.h // // connects and gets the data from mesos_net REST API interface // #pragma once #include "mesos_component.h" class mesos_event_data { public: enum type { MESOS_UNKNOWN_EVENT, MESOS_API_POST_EVENT, MESOS_STATUS_UPDATE_EVENT, MESOS_APP_TERMINATED_EVENT, MESOS_GROUP_CHANGE_SUCCESS_EVENT, MESOS_DEPLOYMENT_SUCCESS_EVENT, MESOS_IGNORED_EVENT }; typedef std::map event_map_t; static event_map_t m_events; mesos_event_data() = delete; mesos_event_data(const std::string& data); mesos_event_data(const mesos_event_data& other); mesos_event_data(mesos_event_data&& other); mesos_event_data& operator=(mesos_event_data&& other); type get_event() const; const std::string& get_data() const; std::string& get_data(); void set_data(const std::string&& data); static std::string get_event_type(const std::string& data); static type get_event_type_from_name(const std::string& name); static type get_event_type(const Json::Value& root); static bool is_ignored(const std::string& evt); private: type m_event; std::string m_data; }; inline mesos_event_data::type mesos_event_data::get_event() const { return m_event; } inline const std::string& mesos_event_data::get_data() const { return m_data; } inline std::string& mesos_event_data::get_data() { return m_data; } inline bool mesos_event_data::is_ignored(const std::string& evt) { return evt == m_events[MESOS_IGNORED_EVENT]; } sysdig-0.8.0/userspace/libsinsp/mesos_http.cpp000066400000000000000000000257241265472057500215560ustar00rootroot00000000000000// // mesos_http.cpp // #ifdef HAS_CAPTURE #include "mesos_http.h" #include "curl/curl.h" #include "curl/easy.h" #include "curl/curlbuild.h" #include "sinsp.h" #include "sinsp_int.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): 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(5000L), m_callback_func(0), m_curl_version(curl_version_info(CURLVERSION_NOW)), m_request(make_request(url, m_curl_version)) { if(!m_sync_curl || !m_select_curl) { throw sinsp_exception("CURL initialization failed."); } ASSERT(m_curl_version); if((m_url.get_scheme() == "https") && (m_curl_version && !(m_curl_version->features | CURL_VERSION_SSL))) { cleanup(); throw sinsp_exception("HTTPS NOT supported"); } } 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_easy_cleanup(curl); curl = 0; m_connected = 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"; } request << "\r\n"; return request.str(); } size_t mesos_http::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; } CURLcode mesos_http::get_data(const std::string& url, std::ostream& os) { g_logger.log(std::string("Retrieving data from ") + url, sinsp_logger::SEV_DEBUG); curl_easy_setopt(m_sync_curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(m_sync_curl, CURLOPT_FOLLOWLOCATION, 1L); if(m_url.get_scheme() == "https") { check_error(curl_easy_setopt(m_sync_curl, CURLOPT_SSL_VERIFYPEER , 0)); } curl_easy_setopt(m_sync_curl, CURLOPT_NOSIGNAL, 1); //Prevent "longjmp causes uninitialized stack frame" bug curl_easy_setopt(m_sync_curl, CURLOPT_ACCEPT_ENCODING, "deflate"); curl_easy_setopt(m_sync_curl, CURLOPT_WRITEFUNCTION, &mesos_http::write_data); 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 { (m_mesos.*parse)(os.str(), m_framework_id); 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("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 to 120 seconds check_error(curl_easy_setopt(m_select_curl, CURLOPT_TCP_KEEPIDLE, 120L)); // interval time between keep-alive probes: 10 seconds 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)) { cleanup(m_select_curl); throw sinsp_exception("Error obtaining socket: timeout."); } g_logger.log(std::string("Connected: collecting data from ") + url, sinsp_logger::SEV_DEBUG); } if(m_watch_socket <= 0) { throw sinsp_exception("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("Cannot send request (empty)."); } if(m_watch_socket < 0) { m_connected = false; throw sinsp_exception("Cannot create watch socket (invalid socket)."); } size_t iolen = send(m_watch_socket, m_request.c_str(), m_request.size(), 0); if(iolen <= 0) { cleanup(m_select_curl); throw sinsp_exception("Socket connection error."); } else if(!wait(1)) { cleanup(); throw sinsp_exception("Error: timeout."); } ASSERT (m_request.size() == iolen); g_logger.log(m_request, sinsp_logger::SEV_DEBUG); } void mesos_http::add_data_chunk(std::istringstream&& chunk_str) { std::string chunk; while(std::getline(chunk_str, chunk)) { std::string::size_type sz = chunk.size(); if(sz) { chunk.erase(std::remove_if(chunk.begin(), chunk.end(), [](char c) { return c == '\r' || c == '\n'; }), chunk.end()); if(chunk == "HTTP/1.1 200 OK") // new request, start fresh { m_data_buf.clear(); continue; } sz = chunk.size(); if(sz) { bool json_begin = (chunk[0] == '{'); if(json_begin) { m_data_buf.append(chunk); sz = m_data_buf.size(); bool json_end = m_data_buf[sz - 1] == '}'; if(json_end && try_parse(m_data_buf)) { (m_mesos.*m_callback_func)(std::move(m_data_buf), m_framework_id); m_data_buf.clear(); } } } } } } void mesos_http::extract_data(const std::string& data) { if(data.size()) { const std::string delim = "\r\n\r\n"; std::string::size_type begin_pos = 0; std::string::size_type end_pos = data.find(delim); if(end_pos == std::string::npos) { add_data_chunk(std::istringstream(data)); return; } while(true) { add_data_chunk(std::istringstream(data.substr(begin_pos, end_pos - begin_pos))); begin_pos = end_pos + delim.size(); end_pos = data.find(delim, begin_pos); if(end_pos == std::string::npos) { add_data_chunk(std::istringstream(data.substr(begin_pos))); break; } } } } bool mesos_http::on_data() { if(!m_callback_func) { throw sinsp_exception("Cannot parse data (parse function null)."); } size_t iolen = 0; std::vector buf; std::string data; try { do { size_t iolen = 0; int count = 0; int ioret = 0; ioret = ioctl(m_watch_socket, FIONREAD, &count); if(ioret >= 0 && count) { if(count > static_cast(buf.size())) { buf.resize(count); } iolen = recv(m_watch_socket, &buf[0], count, 0); } else { if(ioret < 0) { std::string err = strerror(errno); g_logger.log("mesos_http::on_data() error : " + err, sinsp_logger::SEV_ERROR); return false; } break; } if(iolen > 0) { data.append(&buf[0], iolen <= buf.size() ? iolen : buf.size()); } else if(iolen == 0) { g_logger.log("Mesos or Marathon API connection closed", sinsp_logger::SEV_ERROR); m_connected = false; return false; } } while(iolen && errno != CURLE_AGAIN); if(data.size()) { extract_data(data); } } catch(sinsp_exception& ex) { g_logger.log(std::string("Data receive error: ").append(ex.what()), sinsp_logger::SEV_ERROR); return false; } return true; } 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("Error parsing tasks.\nJSON:\n---\n" + os.str() + "\n---", sinsp_logger::SEV_ERROR); } } catch(std::exception& ex) { g_logger.log(std::string("Error parsing tasks:") + ex.what(), sinsp_logger::SEV_ERROR); } return labels; } #endif // HAS_CAPTURE sysdig-0.8.0/userspace/libsinsp/mesos_http.h000066400000000000000000000105711265472057500212150ustar00rootroot00000000000000// // mesos_http.h // #pragma once #ifdef HAS_CAPTURE #include "curl/curl.h" #include "uri.h" #include "json/json.h" #include #include #include #include class mesos; class mesos_http { public: typedef std::shared_ptr ptr_t; typedef void (mesos::*callback_func_t)(std::string&&, const std::string&); mesos_http(mesos& m, const uri& url); 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); 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(); static std::string make_request(uri url, curl_version_info_data* m_curl_version = 0); bool try_parse(const std::string& json); private: void send_request(); static size_t write_data(void *ptr, size_t size, size_t nmemb, void *cb); 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; friend class mesos; typedef std::vector pos_vec_t; void add_data_chunk(std::istringstream&& chunk_str); void extract_data(const std::string& data); void handle_data(); // probably belongs to utils template struct my_equal { my_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; }; // find substring (case insensitive) 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(), my_equal(loc) ); if(it != str1.end()) { return it - str1.begin(); } return -1; // not found } }; 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 bool mesos_http::try_parse(const std::string& json) { Json::Value root; return Json::Reader().parse(json, root, true); } 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; } #endif // HAS_CAPTURE sysdig-0.8.0/userspace/libsinsp/mesos_state.cpp000066400000000000000000000201221265472057500217020ustar00rootroot00000000000000// // 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) : m_is_captured(is_captured), m_marathon_changed(true) { } mesos_framework::task_ptr_t mesos_state_t::get_task(const std::string& uid) { 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_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; } 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; } bool mesos_state_t::parse_groups(std::string&& json, const std::string& framework_id) { Json::Value root; Json::Reader reader; if(reader.parse(json, root, false) && !root["id"].isNull()) { add_group(root, 0, framework_id); return true; } else { throw sinsp_exception("Marathon groups parsing failed (Invalid JSON)."); } } 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) { 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)); add_or_replace_group(pg, to_group); Json::Value apps = group["apps"]; if(!apps.isNull()) { for(const auto& app : apps) { Json::Value app_id = app["id"]; if(!app_id.isNull()) { 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 occured 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(std::string&& json, const std::string& framework_id) { Json::Value root; Json::Reader reader; if(reader.parse(json, root, false)) { Json::Value apps = root["apps"]; if(!apps.isNull()) { for(const auto& app : apps) { add_app(app, framework_id); } } else { g_logger.log("No apps found.", sinsp_logger::SEV_WARNING); } } else { g_logger.log(json, sinsp_logger::SEV_DEBUG); 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; 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) { g_logger.log("Added app [" + id + "] to Marathon group: [" + group_id + ']', sinsp_logger::SEV_DEBUG); 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.8.0/userspace/libsinsp/mesos_state.h000066400000000000000000000153771265472057500213670ustar00rootroot00000000000000// // 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 // // state // class mesos_state_t { public: mesos_state_t(bool is_captured = 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); // // 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); 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_changed(bool changed = true) { m_marathon_changed = changed; } bool get_marathon_changed() const { return m_marathon_changed; } // // Marathon apps // void parse_apps(std::string&& 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) { 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 groups // bool parse_groups(std::string&& 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 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); // // state // void clear_mesos(); void clear_marathon(); void print_groups() 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; mesos_slaves m_slaves; marathon_groups m_groups; bool m_is_captured; bool m_marathon_changed; std::unordered_multimap m_marathon_task_cache; friend class marathon_dispatcher; }; // // 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) { m_frameworks.push_back(framework); } inline void mesos_state_t::emplace_framework(mesos_framework&& framework) { m_frameworks.emplace_back(std::move(framework)); } inline void mesos_state_t::add_or_replace_task(mesos_framework& framework, mesos_task::ptr_t task) { framework.add_or_replace_task(task); } 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); } // // 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) { m_slaves.push_back(slave); } inline void mesos_state_t::emplace_slave(mesos_slave&& slave) { m_slaves.emplace_back(std::move(slave)); } // // 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(); } sysdig-0.8.0/userspace/libsinsp/mesosget.cpp000066400000000000000000000033771265472057500212170ustar00rootroot00000000000000// // mesosget.cpp // // extracts needed data from the mesos REST API interface, // translates it to protobuf and prints the result in human readable format // // usage: mesosget [http://localhost:80] [v1] // #include "sinsp.h" #include "mesos_proto.h" #include "mesos_common.h" #include "mesos_http.h" #include "mesos.h" #include "Poco/FileStream.h" #include using namespace Poco; sinsp_logger g_logger; void print_groups(const ::google::protobuf::RepeatedPtrField< ::draiosproto::marathon_group >& groups) { for(auto group : groups) { std::cout << group.id() << std::endl; for(const auto& app : group.apps()) { std::cout << '\t' << app.id() << std::endl; for(const auto& task : app.task_ids()) { std::cout << "\t\t" << task << std::endl; } } for(auto subgroup : group.groups()) { print_groups(groups); } } } void print_proto(mesos& m, const std::string& fname) { draiosproto::metrics met; mesos_proto(met).get_proto(m.get_state()); //FileOutputStream fos("/home/alex/sysdig/agent/experiments/mesos/" + fname + ".protodump"); //fos << met.DebugString(); std::cout << met.DebugString() << std::endl; //std::cout << "++++" << std::endl; //print_groups(met.mesos().groups()); //std::cout << "----" << std::endl; } int main(int argc, char** argv) { std::string ip_addr = "52.90.231.127"; std::vector marathon_uris; marathon_uris.push_back("http://" + ip_addr + ":8080"); mesos m("http://" + ip_addr + ":5050", "/master/state", marathon_uris, mesos::default_groups_api, mesos::default_apps_api, mesos::default_watch_api); //print_proto(m, ip_addr); //m.refresh(true); //print_proto(m, ip_addr); while(true) { //print_proto(m, ip_addr); m.refresh(false); m.watch(); sleep(5); } return 0; } sysdig-0.8.0/userspace/libsinsp/parsers.cpp000066400000000000000000002414501265472057500210440ustar00rootroot00000000000000/* 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 "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; sinsp_parser::sinsp_parser(sinsp *inspector) : m_inspector(inspector), m_tmp_evt(m_inspector), m_fd_listener(NULL) { m_k8s_metaevents_state.m_piscapevt = (scap_evt*)new char[SP_EVT_BUF_SIZE]; m_k8s_metaevents_state.m_piscapevt->type = PPME_K8S_E; m_k8s_metaevents_state.m_metaevt.m_inspector = m_inspector; m_k8s_metaevents_state.m_metaevt.m_info = &(g_infotables.m_event_info[PPME_SYSDIGEVENT_X]); m_k8s_metaevents_state.m_metaevt.m_pevt = NULL; m_k8s_metaevents_state.m_metaevt.m_cpuid = 0; m_k8s_metaevents_state.m_metaevt.m_evtnum = 0; m_k8s_metaevents_state.m_metaevt.m_pevt = m_k8s_metaevents_state.m_piscapevt; m_k8s_metaevents_state.m_metaevt.m_fdinfo = NULL; } sinsp_parser::~sinsp_parser() { for(uint32_t j = 0; j < m_protodecoders.size(); j++) { delete m_protodecoders[j]; } m_protodecoders.clear(); delete[] m_k8s_metaevents_state.m_piscapevt; } /////////////////////////////////////////////////////////////////////////////// // PROCESSING ENTRY POINT /////////////////////////////////////////////////////////////////////////////// void sinsp_parser::process_event(sinsp_evt *evt) { uint16_t etype = evt->m_pevt->type; bool is_live = m_inspector->m_islive; // // 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 // // Filtering // #if defined(HAS_FILTERING) && defined(HAS_CAPTURE_FILTERING) bool do_filter_later = false; if(m_inspector->m_filter) { ppm_event_flags eflags = evt->get_flags(); if(eflags & EF_MODIFIES_STATE) { do_filter_later = true; } else { if(m_inspector->m_filter->run(evt) == false) { if(evt->m_tinfo != NULL) { if(!(eflags & EF_SKIPPARSERESET || etype == PPME_SCHEDSWITCH_6_E)) { evt->m_tinfo->m_lastevent_type = PPM_SC_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: store_event(evt); 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: 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); 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_SYSCALL_CHROOT_X: parse_chroot_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->m_filter) { if(m_inspector->m_filter->run(evt) == false) { evt->m_filtered_out = true; return; } } evt->m_filtered_out = false; } #endif // // Offline captures can prodice 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_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); 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; 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. // 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; // // 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)); tid = *(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)); tid = *(int64_t *)parinfo->m_val; } // // 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 command arguments from the parent tinfo.m_args = ptinfo->m_args; // Copy the root from the parent tinfo.m_root = ptinfo->m_root; } 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_args = ptinfo->m_args; tinfo.m_root = ptinfo->m_root; } else { // // Parent not found in proc, use the event data // 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->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; } // // 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 // if(!(tinfo.m_flags & PPM_CL_CLONE_THREAD)) { 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(); } //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 working directory parinfo = evt->get_param(6); tinfo.set_cwd(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 uid 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_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->m_islive); 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 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(); // 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: // 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; // Get the working directory parinfo = evt->get_param(6); evt->m_tinfo->set_cwd(parinfo->m_val, parinfo->m_len); // 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: // 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: // 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); if(evt->m_tinfo->m_container_id.empty()) { m_inspector->m_container_manager.resolve_container(evt->m_tinfo, m_inspector->m_islive); } 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 flag // bool inverted = ((evt->m_tinfo->m_flags & PPM_CL_CLONE_INVERTED) != 0); evt->m_tinfo->m_flags = PPM_CL_ACTIVE; 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 evt->m_tinfo->m_ainfo->clear_role_flags(); #endif 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; } } } } void schedule_more_k8s_evts(sinsp* inspector, void* data) { #ifdef HAS_CAPTURE ASSERT(data); bool good_event = false; k8s_metaevents_state* state = (k8s_metaevents_state*)data; if(state->m_new_group == true) { state->m_new_group = false; inspector->add_meta_event(&state->m_metaevt); return; } k8s* k8s_client = inspector->get_k8s_client(); ASSERT(k8s_client); if(!k8s_client->get_capture_events().size()) { g_logger.log("K8S event scheduled but no events available." "All pending K8S event request are cancelled.", sinsp_logger::SEV_ERROR); state->m_new_group = false; state->m_n_additional_k8s_events_to_add = 0; inspector->remove_meta_event_callback(); return; } string payload = k8s_client->dequeue_capture_event(); std::size_t tot_len = sizeof(scap_evt) + sizeof(uint16_t) + payload.size() + 1; if(tot_len <= SP_EVT_BUF_SIZE) { 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; } else { g_logger.log("K8S event larger than available buffer, will not be recorded. " "This may result in an inaccurate K8S event log.", sinsp_logger::SEV_ERROR); } state->m_n_additional_k8s_events_to_add--; if(state->m_n_additional_k8s_events_to_add == 0) { inspector->remove_meta_event_callback(); } else if(good_event) { inspector->add_meta_event(&state->m_metaevt); } #endif // HAS_CAPTURE } void sinsp_parser::schedule_k8s_events(sinsp_evt *evt) { #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 = evt->get_tid(); 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_k8s_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 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); // // 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; } fdi.m_openflags = flags; fdi.add_filename(fullpath); // // 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_create(evt, fullpath); } } // // 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 != 16 && // AF_NETLINK, used by processes to talk to the kernel domain != 10 && // IPv6 domain != 17) // AF_PACKET, used for packet capture { // // IPv6 will go here // ASSERT(false); } } #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; } // // 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); } 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))) { 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); #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) { 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; } 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; } 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_flags(); 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) { 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) { 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; // // 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_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)); // // 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)); // // 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)); // // 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; // // 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; } 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_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; } } 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); ASSERT(m_inspector->m_k8s_client); m_inspector->m_k8s_client->simulate_watch_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) { 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::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.8.0/userspace/libsinsp/parsers.h000066400000000000000000000120521265472057500205030ustar00rootroot00000000000000/* 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 k8s_metaevents_state { public: bool m_new_group; uint32_t m_n_additional_k8s_events_to_add; sinsp_evt m_metaevt; scap_evt* m_piscapevt; }; 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(sinsp_evt *evt); // // Protocol decoders callback lists // vector m_open_callbacks; vector m_connect_callbacks; private: // // Helpers // bool reset(sinsp_evt *evt); 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); 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); void parse_cpu_hotplug_enter(sinsp_evt* evt); void parse_k8s_evt(sinsp_evt *evt); void parse_chroot_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; sinsp_fd_listener* m_fd_listener; // // The protocol decoders allocated by this parser // vector m_protodecoders; k8s_metaevents_state m_k8s_metaevents_state; stack m_tmp_events_buffer; friend class sinsp_analyzer; friend class sinsp_analyzer_fd_listener; friend class sinsp_protodecoder; }; sysdig-0.8.0/userspace/libsinsp/procinfo_test.cpp000066400000000000000000000100521265472057500222330ustar00rootroot00000000000000/* 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.8.0/userspace/libsinsp/protodecoder.cpp000066400000000000000000000153101265472057500220500ustar00rootroot00000000000000/* 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) { 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.8.0/userspace/libsinsp/protodecoder.h000066400000000000000000000076711265472057500215300ustar00rootroot00000000000000/* 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.8.0/userspace/libsinsp/settings.h000066400000000000000000000045561265472057500206760ustar00rootroot00000000000000/* 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 . */ // // 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 // // Is csysdig functionality included? // #define CSYSDIG #ifdef _WIN32 #define NOCURSESUI #endif // // 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 sysdig-0.8.0/userspace/libsinsp/sinsp.cpp000066400000000000000000001012771265472057500205230ustar00rootroot00000000000000/* 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 "filter.h" #include "filterchecks.h" #include "chisel.h" #include "cyclewriter.h" #include "protodecoder.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_metaevt = NULL; m_skipped_evt = 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; #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_isdebug_enabled = false; m_isfatfile_enabled = false; m_hostname_and_port_resolution_enabled = true; m_output_time_flag = 'h'; m_max_evt_output_len = 0; m_filesize = -1; 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_next_flush_time_ns = 0; m_last_procrequest_tod = 0; m_get_procs_cpu_from_driver = 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_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; } 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::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(this->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; // // 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(m_islive == false) { 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) { break; } else { ncnt++; continue; } } else { break; } } // // Rewind and consume the exact number of events // scap_fseek(m_h, off); for(uint32_t j = 0; j < ncnt; j++) { sinsp_evt* tevt; next(&tevt); } } if(m_islive == false || 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_islive) { 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"); m_islive = true; // // Reset the thread manager // m_thread_manager->clear(); // // Start the capture // scap_open_args oargs; 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; } 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(string filename) { char error[SCAP_LASTERR_SIZE] = {0}; m_islive = false; if(filename == "") { open(); return; } m_input_filename = filename; g_logger.log("starting offline capture"); // // Reset the thread manager // m_thread_manager->clear(); // // Start the capture // scap_open_args oargs; oargs.fname = filename.c_str(); oargs.proc_callback = NULL; oargs.proc_callback_context = NULL; oargs.import_users = m_import_users; m_h = scap_open(oargs, error); if(m_h == NULL) { throw sinsp_exception(error); } m_filesize = get_file_size(filename, error); if(m_filesize < 0) { throw sinsp_exception(error); } init(); } 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; } 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; } #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); } 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; } } 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 // if(m_machine_info == NULL) { 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); 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; 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(m_islive) { 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_and_repeat(sinsp_evt *metaevt) { m_metaevt = metaevt; m_skipped_evt = &m_evt; } 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; } } 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; if(m_skipped_evt) { m_metaevt = m_skipped_evt; m_skipped_evt = NULL; } else { m_metaevt = NULL; } if(m_meta_event_callback != NULL) { m_meta_event_callback(this, m_meta_event_callback_data); } } 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 { m_lasterr = scap_getlasterr(m_h); } return res; } } uint64_t ts = evt->get_ts(); if(m_firstevent_ts == 0) { m_firstevent_ts = ts; } // // If required, retrieve the processes cpu from the kernel // if(m_get_procs_cpu_from_driver && m_islive) { 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(m_islive) { m_thread_manager->remove_inactive_threads(); m_container_manager.remove_inactive_containers(); if(m_k8s_client) { update_kubernetes_state(); } } #endif // HAS_ANALYZER // // Deleayed removal of the fd, so that // things like exit() or close() can be parsed. // uint32_t nfdr = (uint32_t)m_fds_to_remove->size(); if(nfdr != 0) { sinsp_threadinfo* ptinfo = get_thread(m_tid_of_fd_to_remove, true, true); if(!ptinfo) { ASSERT(false); return res; } for(uint32_t j = 0; j < nfdr; j++) { ptinfo->remove_fd(m_fds_to_remove->at(j)); } m_fds_to_remove->clear(); } #ifdef SIMULATE_DROP_MODE bool sd = false; bool sw = false; if(m_analyzer) { m_analyzer->m_configuration->set_analyzer_sample_len_ns(500000000); } sd = should_drop(evt, &m_isdropping, &sw); #endif // // Run the state engine // #ifdef SIMULATE_DROP_MODE if(!sd || m_isdropping) { m_parser->process_event(evt); } if(sd && !m_isdropping) { *evt = NULL; return SCAP_TIMEOUT; } #else m_parser->process_event(evt); #endif // // If needed, dump the event to file // if(NULL != m_dumper) { if(m_meta_evt_pending) { m_meta_evt_pending = false; res = scap_dump(m_h, m_dumper, m_meta_evt.m_pevt, m_meta_evt.m_cpuid, 0); if(SCAP_SUCCESS != res) { throw sinsp_exception(scap_getlasterr(m_h)); } } #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) { //res = scap_number_of_bytes_to_write(evt->m_pevt, evt->m_cpuid, &bytes_to_write); 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; } } res = scap_dump(m_h, m_dumper, evt->m_pevt, 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) { *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() { ASSERT(m_h); return scap_event_get_num(m_h); } 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(scap_set_snaplen(m_h, snaplen) != SCAP_SUCCESS) { // // We know that setting the snaplen on a file doesn't do anything and // we're ok with it. // if(m_islive) { 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_islive) { 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_islive) { 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(const string& filter) { if(m_filter != NULL) { ASSERT(false); throw sinsp_exception("filter can only be set once"); } m_filter = new sinsp_filter(this, filter); m_filterstring = filter; } const string sinsp::get_filter() { return m_filterstring; } #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) { g_logger.add_callback_log(cb); } 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; } 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_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::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_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_k8s_client(string* api_server, string* ssl_cert) { ASSERT(api_server); m_k8s_api_server = api_server; m_k8s_api_cert = ssl_cert; if(m_k8s_client == NULL) { g_logger.log("Fetching initial k8s state", sinsp_logger::SEV_INFO); bool is_live = !m_k8s_api_server->empty(); m_k8s_client = new k8s(*m_k8s_api_server, is_live ? true : false, // watch false, // don't run watch in thread is_live ? true : false, // capture "/api/v1/", m_k8s_api_cert ? *m_k8s_api_cert : string("") ); } } void sinsp::update_kubernetes_state() { ASSERT(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; if(m_k8s_client->is_alive()) { uint64_t delta = sinsp_utils::get_current_time_ns(); m_k8s_client->watch(); this->m_parser->schedule_k8s_events(&m_meta_evt); delta = sinsp_utils::get_current_time_ns() - delta; g_logger.format(sinsp_logger::SEV_INFO, "Updating Kubernetes state took %" PRIu64 " ms", delta / 1000000LL); } else { g_logger.format(sinsp_logger::SEV_WARNING, "Kubernetes connection not active anymore, retrying"); delete m_k8s_client; m_k8s_client = NULL; init_k8s_client(m_k8s_api_server, m_k8s_api_cert); } } } /////////////////////////////////////////////////////////////////////////////// // 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.8.0/userspace/libsinsp/sinsp.h000066400000000000000000000577311265472057500201750ustar00rootroot00000000000000/* 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) #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 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" #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; 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: 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 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 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(); #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 Returns true if the current capture is live. */ inline bool is_live() { return m_islive; } /*! \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 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 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_client(string* api_server, string* ssl_cert); k8s* get_k8s_client() const { return m_k8s_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; } // // 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_and_repeat(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 refresh_ifaddr_list(); VISIBILITY_PRIVATE // Doxygen doesn't understand VISIBILITY_PRIVATE #ifdef _DOXYGEN private: #endif 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 update_kubernetes_state(); static int64_t get_file_size(const std::string& fname, char *error); static std::string get_error_desc(const std::string& msg = ""); scap_t* m_h; uint32_t m_nevts; int64_t m_filesize; bool m_islive; string m_input_filename; bool m_isdebug_enabled; bool m_isfatfile_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_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; sinsp_network_interfaces* m_network_interfaces; sinsp_thread_manager* m_thread_manager; sinsp_container_manager m_container_manager; // // Kubernetes stuff // string* m_k8s_api_server; string* m_k8s_api_cert; k8s* m_k8s_client; uint64_t m_k8s_last_watch_time_ns; // // 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; 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 // // Protocol decoding state // vector m_decoders_reset_list; // // 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 sinsp_evt* m_metaevt; sinsp_evt* m_skipped_evt; 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; #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_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_event; friend class sinsp_filter_check_k8s; template friend class sinsp_connection_manager; }; // // Macros for enable/disable k8s threading // Used to eliminate mutex locking when running single-threaded // #ifndef HAS_CAPTURE #ifndef K8S_DISABLE_THREAD #define K8S_DISABLE_THREAD #endif #endif #ifndef K8S_DISABLE_THREAD #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.8.0/userspace/libsinsp/sinsp.vcxproj000066400000000000000000000163251265472057500214330ustar00rootroot00000000000000 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.8.0/userspace/libsinsp/sinsp.vcxproj.filters000066400000000000000000000100121265472057500230650ustar00rootroot00000000000000 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.8.0/userspace/libsinsp/sinsp_errno.h000066400000000000000000000226501265472057500213720ustar00rootroot00000000000000/* 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.8.0/userspace/libsinsp/sinsp_int.h000066400000000000000000000066751265472057500210500ustar00rootroot00000000000000/* 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; // // 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_create(sinsp_evt* evt, const string& fullpath) = 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; }; sysdig-0.8.0/userspace/libsinsp/sinsp_signal.h000066400000000000000000000031751265472057500215230ustar00rootroot00000000000000/* 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.8.0/userspace/libsinsp/sinsp_test.cpp000066400000000000000000000020441265472057500215520ustar00rootroot00000000000000/* 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.8.0/userspace/libsinsp/stats.cpp000066400000000000000000000051171265472057500205210ustar00rootroot00000000000000/* 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.8.0/userspace/libsinsp/stats.h000066400000000000000000000027771265472057500201770ustar00rootroot00000000000000/* 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.8.0/userspace/libsinsp/table.cpp000066400000000000000000000670301265472057500204540ustar00rootroot00000000000000/* 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) { ppm_cmp_operator 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, bool print_to_stdout) { 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_print_to_stdout = print_to_stdout; 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; } 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) { m_use_defaults = use_defaults; // // 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 != "") { m_filter = new sinsp_filter(m_inspector, filter); } ////////////////////////////////////////////////////////////////////////////////////// // Extract the tokens ////////////////////////////////////////////////////////////////////////////////////// m_premerge_extractors.clear(); for(auto vit : *entries) { sinsp_filter_check* chk = g_filterlist.new_filter_check_from_fldname(vit.m_field, m_inspector, false); if(chk == NULL) { throw sinsp_exception("invalid field name " + vit.m_field); } chk->m_aggregation = (sinsp_field_aggregation)vit.m_aggregation; m_chks_to_free.push_back(chk); chk->parse_field_name(vit.m_field.c_str(), true); 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); 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::stdout_print(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::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_print_to_stdout) { #endif stdout_print(m_sample_data, time_delta); #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(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; 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) { 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(rownum >= m_sample_data->size()) { ASSERT(m_sample_data->size() == 0); 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.8.0/userspace/libsinsp/table.h000066400000000000000000000170071265472057500201200ustar00rootroot00000000000000/* 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_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, }; sinsp_table(sinsp* inspector, tabletype type, uint64_t refresh_interval_ns, bool print_to_stdout); ~sinsp_table(); void configure(vector* entries, const string& filter, bool use_defaults); 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); 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; 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(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 stdout_print(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_types; 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; bool m_print_to_stdout; friend class curses_table; friend class sinsp_cursesui; }; sysdig-0.8.0/userspace/libsinsp/third-party/000077500000000000000000000000001265472057500211225ustar00rootroot00000000000000sysdig-0.8.0/userspace/libsinsp/third-party/jsoncpp/000077500000000000000000000000001265472057500225765ustar00rootroot00000000000000sysdig-0.8.0/userspace/libsinsp/third-party/jsoncpp/json/000077500000000000000000000000001265472057500235475ustar00rootroot00000000000000sysdig-0.8.0/userspace/libsinsp/third-party/jsoncpp/json/json-forwards.h000066400000000000000000000214031265472057500265160ustar00rootroot00000000000000/// 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.8.0/userspace/libsinsp/third-party/jsoncpp/json/json.h000066400000000000000000001732051265472057500247010ustar00rootroot00000000000000/// 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.8.0/userspace/libsinsp/third-party/jsoncpp/jsoncpp.cpp000066400000000000000000004226671265472057500247770ustar00rootroot00000000000000/// 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.8.0/userspace/libsinsp/third-party/tinydir.h000066400000000000000000000206671265472057500227700ustar00rootroot00000000000000/* 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.8.0/userspace/libsinsp/threadinfo.cpp000066400000000000000000000571271265472057500215160ustar00rootroot00000000000000/* 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" 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; init(); } sinsp_threadinfo::sinsp_threadinfo(sinsp *inspector) : m_fdtable(inspector) { m_inspector = inspector; init(); } void sinsp_threadinfo::init() { m_pid = (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_lastevent_data = 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(); } 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(); } } } } void sinsp_threadinfo::compute_program_hash() { string phs = m_exe; for(auto arg = m_args.begin(); arg != m_args.end(); ++arg) { phs += *arg; } phs += m_container_id; m_program_hash = std::hash()(phs); } 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; 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; 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_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: newfdi->m_name = fdi->info.fname; 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_comm = pi->comm; m_exe = pi->exe; set_args(pi->args, pi->args_len); 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; set_cgroups(pi->cgroups, pi->cgroups_len); m_root = pi->root; ASSERT(m_inspector); m_inspector->m_container_manager.resolve_container(this, m_inspector->m_islive); // // 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; } 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; } } string sinsp_threadinfo::get_env(const string& name) const { for(const auto& env_var : m_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"; } 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() { sinsp_threadinfo* tinfo = get_cwd_root(); 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_cwd_root(); 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; if(tinfo->m_cwd[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; } 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 /////////////////////////////////////////////////////////////////////////////// // 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 } sysdig-0.8.0/userspace/libsinsp/threadinfo.h000066400000000000000000000253241265472057500211550ustar00rootroot00000000000000/* 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 class sinsp_delays_info; class sinsp_threadtable_listener; class thread_analyzer_info; 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 full name of the process containing this thread, e.g. "/bin/top". */ string get_exe(); /*! \brief Return the working directory of the process containing this thread. */ string get_cwd(); /*! \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) const; /*! \brief Return true if this is a process' main thread. */ inline bool is_main_thread() { 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(); // // 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. string m_comm; ///< Command name (e.g. "top") string m_exe; ///< argv[0] (e.g. "sshd: user@pts/4") 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; // // 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. 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(); // 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; size_t m_program_hash; 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 lua_cbacks; }; /*@}*/ 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(); 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); 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; }; sysdig-0.8.0/userspace/libsinsp/tuples.h000066400000000000000000000043111265472057500203370ustar00rootroot00000000000000/* 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 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.8.0/userspace/libsinsp/uri.cpp000066400000000000000000000034501265472057500201600ustar00rootroot00000000000000// // uri.h // // URI utilities // #include "uri.h" #include uri::uri(std::string str): m_port(0) { m_scheme = extract_protocol(str); m_query = extract_query(str); m_path = extract_path(str); std::string auth = extract_auth(str); m_password = extract_password(auth); m_user = auth; m_port = extract_port(str); m_host = str; } std::string uri::tail_chunk(std::string &subject, std::string delimiter, bool keep_delim) { auto delimiter_location = subject.find(delimiter); auto delimiter_length = delimiter.length(); std::string output; if(delimiter_location != std::string::npos) { auto start = keep_delim ? delimiter_location : delimiter_location + delimiter_length; auto end = subject.length() - start; output = subject.substr(start, end); subject = subject.substr(0, delimiter_location); } return output; } std::string uri::head_chunk(std::string &subject, std::string delimiter) { auto delimiter_location = subject.find(delimiter); auto delimiter_length = delimiter.length(); std::string output; if(delimiter_location != std::string::npos) { output = subject.substr(0, delimiter_location); subject = subject.substr(delimiter_location + delimiter_length, subject.length() - (delimiter_location + delimiter_length)); } return output; } int uri::extract_port(std::string& hostport) { int m_port; std::string portstring = tail_chunk(hostport, ":"); try { m_port = atoi(portstring.c_str()); } catch (std::exception e) { m_port = 0; } return m_port; } std::string uri::to_string() const { std::ostringstream ostr; ostr << m_scheme << "://"; if(!m_user.empty()) { ostr << m_user << ':' << m_password << '@'; } ostr << m_host; if(m_port) { ostr << ':' << m_port; } ostr << m_path; if(!m_query.empty()) { ostr << '?' << m_query; } return ostr.str(); } sysdig-0.8.0/userspace/libsinsp/uri.h000066400000000000000000000057541265472057500176360ustar00rootroot00000000000000// // uri.h // // URI utilities // #pragma once #include class uri { public: uri() = delete; uri(std::string str); const std::string& get_scheme() const; void set_scheme(const std::string& scheme); const std::string& get_user() const; void set_user(const std::string& user); const std::string& get_password() const; void set_password(const std::string& password); const std::string& get_host() const; void set_host(const std::string& host); const std::string& get_path() const; void set_path (const std::string& path); const std::string& get_query() const; void set_query(const std::string& query); int get_port() const; void set_port(int port); bool is_secure() const; std::string get_credentials() const; std::string to_string() const; private: std::string tail_chunk(std::string& subject, std::string delimiter, bool keep_delim = false); std::string head_chunk(std::string& subject, std::string delimiter); int extract_port(std::string& hostport); std::string extract_path(std::string& str); std::string extract_protocol(std::string& str); std::string extract_query(std::string& str); std::string extract_password(std::string& userpass); std::string extract_auth(std::string& str); std::string m_scheme, m_user, m_password, m_host, m_path, m_query; int m_port; }; inline const std::string& uri::get_scheme() const { return m_scheme; } inline void uri::set_scheme(const std::string& scheme) { m_scheme = scheme; } inline const std::string& uri::get_user() const { return m_user; } inline void uri::set_user(const std::string& user) { m_user = user; } inline const std::string& uri::get_password() const { return m_password; } inline void uri::set_password(const std::string& password) { m_password = password; } inline const std::string& uri::get_host() const { return m_host; } inline void uri::set_host(const std::string& host) { m_host = host; } inline const std::string& uri::get_path() const { return m_path; } inline void uri::set_path(const std::string& path) { m_path = path; } inline const std::string& uri::get_query() const { return m_query; } inline void uri::set_query(const std::string& query) { m_query = query; } inline int uri::get_port() const { return m_port; } inline void uri::set_port(int port) { m_port = port; } inline std::string uri::extract_path(std::string& str) { return tail_chunk(str, "/", true); } inline std::string uri::extract_protocol(std::string& str) { return head_chunk(str, "://"); } inline std::string uri::extract_query(std::string& str) { return tail_chunk(str, "?"); } inline std::string uri::extract_password(std::string &userpass) { return tail_chunk(userpass, ":"); } inline std::string uri::extract_auth(std::string& str) { return head_chunk(str, "@"); } inline bool uri::is_secure() const { return "https" == m_scheme; } 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; } sysdig-0.8.0/userspace/libsinsp/utils.cpp000066400000000000000000000672731265472057500205360ustar00rootroot00000000000000/* 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 #else #pragma comment(lib, "Ws2_32.lib") #include #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 "json/json.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_DEBUG); #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) { 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; } #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 /////////////////////////////////////////////////////////////////////////////// // 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); } /////////////////////////////////////////////////////////////////////////////// // 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)); } void replace_in_place(string &s, const string &search, const string &replace) { for(size_t pos = 0; ; pos += replace.length()) { // Locate the substring to replace pos = s.find(search, pos); if(pos == string::npos ) break; // Replace by erasing and inserting s.erase(pos, search.length()); s.insert(pos, replace ); } } void replace_in_place(string& str, string& substr_to_replace, string& new_substr) { size_t index = 0; uint32_t nsize = (uint32_t)substr_to_replace.size(); while (true) { index = str.find(substr_to_replace, index); if (index == string::npos) break; str.replace(index, nsize, new_substr); index += nsize; } } /////////////////////////////////////////////////////////////////////////////// // sinsp_numparser implementation /////////////////////////////////////////////////////////////////////////////// uint32_t sinsp_numparser::parseu8(const string& str) { uint32_t res; char temp; if(std::sscanf(str.c_str(), "%" PRIu8 "%c", &res, &temp) != 1) { throw sinsp_exception(str + " is not a valid number"); } return res; } int32_t sinsp_numparser::parsed8(const string& str) { int32_t res; char temp; if(std::sscanf(str.c_str(), "%" PRId8 "%c", &res, &temp) != 1) { throw sinsp_exception(str + " is not a valid number"); } return res; } uint32_t sinsp_numparser::parseu16(const string& str) { uint32_t res; char temp; if(std::sscanf(str.c_str(), "%" PRIu16 "%c", &res, &temp) != 1) { throw sinsp_exception(str + " is not a valid number"); } return res; } int32_t sinsp_numparser::parsed16(const string& str) { int32_t res; char temp; if(std::sscanf(str.c_str(), "%" PRId16 "%c", &res, &temp) != 1) { throw sinsp_exception(str + " is not a valid number"); } return 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& root, const std::string& name) { std::string ret; Json::Value json_val = root[name]; if(!json_val.isNull() && json_val.isString()) { ret = json_val.asString(); } return ret; } #if defined(__linux__) /////////////////////////////////////////////////////////////////////////////// // Curl helpers /////////////////////////////////////////////////////////////////////////////// sinsp_curl::sinsp_curl(const std::string& uristr, const std::string& cert): m_curl(curl_easy_init()), m_uri(new uri(uristr)), m_cert(cert), m_timeout(10) { if(!m_curl || !m_uri) { throw sinsp_exception("Cannot initialize CURL."); } } sinsp_curl::~sinsp_curl() { curl_easy_cleanup(m_curl); delete m_uri; } string sinsp_curl::get_data() { std::ostringstream os; if(get_data(os)) { return os.str(); } return ""; } bool sinsp_curl::get_data(std::ostream& os) { CURLcode res = CURLE_OK; curl_easy_setopt(m_curl, CURLOPT_URL, m_uri->to_string().c_str()); curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1L); if(m_uri->is_secure()) { if(m_cert.empty()) { check_error(curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER , 0)); } else { check_error(curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER , 1)); res = curl_easy_setopt(m_curl, CURLOPT_CAINFO, m_cert.c_str()); if(res != CURLE_OK) { os << curl_easy_strerror(res) << std::flush; return false; } } } curl_easy_setopt(m_curl, CURLOPT_NOSIGNAL, 1); //Prevent "longjmp causes uninitialized stack frame" bug curl_easy_setopt(m_curl, CURLOPT_ACCEPT_ENCODING, "deflate"); curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, &sinsp_curl::write_data); curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &os); 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 long http_code = 0; curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &http_code); if(http_code >= 400) { return false; } } return res == CURLE_OK; } void sinsp_curl::set_timeout(long seconds) { m_timeout = seconds; } long sinsp_curl::get_timeout() const { return m_timeout; } 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; } void sinsp_curl::check_error(unsigned ret) { if(ret >= CURL_LAST) { throw sinsp_exception("Invalid CURL return value:" + std::to_string(ret)); } CURLcode res = (CURLcode)ret; if(CURLE_OK != res && CURLE_AGAIN != res) { std::ostringstream os; os << "Error: " << curl_easy_strerror(res); throw sinsp_exception(os.str()); } } #endif // __linux__ sysdig-0.8.0/userspace/libsinsp/utils.h000066400000000000000000000154111265472057500201660ustar00rootroot00000000000000/* 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_evttables; /////////////////////////////////////////////////////////////////////////////// // 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(string name); // // Get the list of filtercheck fields // static void get_filtercheck_fields_info(vector* list); static uint64_t get_current_time_ns(); #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); } }; /////////////////////////////////////////////////////////////////////////////// // 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 /////////////////////////////////////////////////////////////////////////////// string sinsp_gethostname(); /////////////////////////////////////////////////////////////////////////////// // tuples to string /////////////////////////////////////////////////////////////////////////////// // each of these functions uses values in network byte order string ipv4tuple_to_string(ipv4tuple* tuple, bool resolve); string ipv6tuple_to_string(_ipv6tuple* tuple, bool resolve); string ipv4serveraddr_to_string(ipv4serverinfo* addr, bool resolve); 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 string port_to_string(uint16_t port, uint8_t l4proto, bool resolve); /////////////////////////////////////////////////////////////////////////////// // String helpers /////////////////////////////////////////////////////////////////////////////// vector sinsp_split(const string &s, char delim); template string sinsp_join(It begin, It end, char delim); string& ltrim(string &s); string& rtrim(string &s); string& trim(string &s); void replace_in_place(string &s, const string &search, const string &replace); void replace_in_place(string& str, string& substr_to_replace, string& new_substr); /////////////////////////////////////////////////////////////////////////////// // number parser /////////////////////////////////////////////////////////////////////////////// class sinsp_numparser { public: static uint32_t parseu8(const string& str); static int32_t parsed8(const string& str); static uint32_t parseu16(const string& str); static int32_t parsed16(const string& str); static uint32_t parseu32(const string& str); static int32_t parsed32(const string& str); static uint64_t parseu64(const string& str); static int64_t parsed64(const string& str); static bool tryparseu32(const string& str, uint32_t* res); static bool tryparsed32(const string& str, int32_t* res); static bool tryparseu64(const string& str, uint64_t* res); static bool tryparsed64(const 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& root, const std::string& name); /////////////////////////////////////////////////////////////////////////////// // Curl helpers /////////////////////////////////////////////////////////////////////////////// #if defined(__linux__) class uri; class sinsp_curl { public: sinsp_curl(const std::string& uristr, const std::string& cert = ""); ~sinsp_curl(); bool get_data(std::ostream& os); string get_data(); void set_timeout(long seconds); long get_timeout() const; private: static size_t write_data(void *ptr, size_t size, size_t nmemb, void *cb); void check_error(unsigned ret); void* m_curl; uri* m_uri; string m_cert; long m_timeout; }; #endif // __linux__ sysdig-0.8.0/userspace/libsinsp/viewinfo.cpp000066400000000000000000000133351265472057500212120ustar00rootroot00000000000000/* 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 "sinsp_errno.h" #include "sinsp_signal.h" #include "filter.h" #include "filterchecks.h" #include "chisel.h" #include "protodecoder.h" #include /////////////////////////////////////////////////////////////////////////////// // 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) { 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_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_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; } } } 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.8.0/userspace/libsinsp/viewinfo.h000066400000000000000000000105731265472057500206600ustar00rootroot00000000000000/* 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 // // 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) /////////////////////////////////////////////////////////////////////////////// // Column information /////////////////////////////////////////////////////////////////////////////// class sinsp_view_column_info { public: 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) { 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; } 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; }; /////////////////////////////////////////////////////////////////////////////// // 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); void get_col_names_and_sizes(OUT vector* colnames, OUT vector* colsizes); 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; string m_filter; 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; 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.8.0/userspace/sysdig/000077500000000000000000000000001265472057500163325ustar00rootroot00000000000000sysdig-0.8.0/userspace/sysdig/CMakeLists.txt000066400000000000000000000045731265472057500211030ustar00rootroot00000000000000include_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.8.0/userspace/sysdig/chisels/000077500000000000000000000000001265472057500177645ustar00rootroot00000000000000sysdig-0.8.0/userspace/sysdig/chisels/COPYING000066400000000000000000000432541265472057500210270ustar00rootroot00000000000000 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.8.0/userspace/sysdig/chisels/ansiterminal.lua000066400000000000000000000042661265472057500231650ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/around.lua000066400000000000000000000070251265472057500217630ustar00rootroot00000000000000--[[ 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 rang 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 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.8.0/userspace/sysdig/chisels/bottlenecks.lua000066400000000000000000000047421265472057500230130ustar00rootroot00000000000000--[[ 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 else 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.8.0/userspace/sysdig/chisels/common.lua000066400000000000000000000153001265472057500217560ustar00rootroot00000000000000--[[ 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 sysdig-0.8.0/userspace/sysdig/chisels/dkjson.lua000066400000000000000000000637401265472057500217710ustar00rootroot00000000000000 -- 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.8.0/userspace/sysdig/chisels/echo_fds.lua000066400000000000000000000100231265472057500222350ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/fdbytes_by.lua000066400000000000000000000027441265472057500226300ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/fdcount_by.lua000066400000000000000000000044631265472057500226320ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/fdtime_by.lua000066400000000000000000000026351265472057500224370ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/fileslower.lua000066400000000000000000000116241265472057500226460ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/http.lua000066400000000000000000000060511265472057500214500ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/httplog.lua000066400000000000000000000032421265472057500221510ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/httptop.lua000066400000000000000000000112071265472057500221720ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/iobytes.lua000066400000000000000000000032021265472057500221420ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/iobytes_file.lua000066400000000000000000000031021265472057500231400ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/iobytes_net.lua000066400000000000000000000032031265472057500230110ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/list_login_shells.lua000066400000000000000000000064361265472057500242150ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/lscontainers.lua000066400000000000000000000047561265472057500232070ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/lsof.lua000066400000000000000000000054001265472057500214310ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/memcachelog.lua000066400000000000000000000053411265472057500227360ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/netlower.lua000066400000000000000000000110701265472057500223250ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/netstat.lua000066400000000000000000000054211265472057500221530ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/proc_exec_time.lua000066400000000000000000000104661265472057500234630ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/ps.lua000066400000000000000000000053251265472057500211160ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/scallslower.lua000066400000000000000000000101771265472057500230270ustar00rootroot00000000000000--[[ 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 compatable 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) / 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.8.0/userspace/sysdig/chisels/shellshock_detect.lua000066400000000000000000000041751265472057500241650ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/spectrogram.lua000066400000000000000000000100451265472057500230150ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/spy_file.lua000066400000000000000000000072721265472057500223110ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/spy_ip.lua000066400000000000000000000051701265472057500217750ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/spy_logs.lua000066400000000000000000000150311265472057500223260ustar00rootroot00000000000000--[[ 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 compatable 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 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.8.0/userspace/sysdig/chisels/spy_port.lua000066400000000000000000000052351265472057500223530ustar00rootroot00000000000000--[[ 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 fileds 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.8.0/userspace/sysdig/chisels/spy_syslog.lua000066400000000000000000000132411265472057500227030ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/spy_users.lua000066400000000000000000000146571265472057500225400ustar00rootroot00000000000000--[[ 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 compatable 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 else for j = 0, MAX_ANCESTOR_NAVIGATION do fanames[j] = chisel.request_field("proc.aname[" .. j .. "]") fapids[j] = chisel.request_field("proc.apid[" .. j .. "]") end 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.8.0/userspace/sysdig/chisels/stderr.lua000066400000000000000000000053041265472057500217740ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/stdin.lua000066400000000000000000000024141265472057500216110ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/stdout.lua000066400000000000000000000024171265472057500220150ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/subsecoffset.lua000066400000000000000000000105561265472057500231710ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/table_generator.lua000066400000000000000000000104441265472057500236270ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/topconns.lua000066400000000000000000000033461265472057500223400ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/topcontainers_cpu.lua000066400000000000000000000053541265472057500242350ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/topcontainers_error.lua000066400000000000000000000021351265472057500245710ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/topcontainers_file.lua000066400000000000000000000022621265472057500243600ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/topcontainers_net.lua000066400000000000000000000020611265472057500242240ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/topfiles_bytes.lua000066400000000000000000000031701265472057500235230ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/topfiles_errors.lua000066400000000000000000000031451265472057500237130ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/topfiles_time.lua000066400000000000000000000031511265472057500233320ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/topports_server.lua000066400000000000000000000033171265472057500237530ustar00rootroot00000000000000--[[ 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 compatable 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.sproto,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.sproto", "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.8.0/userspace/sysdig/chisels/topprocs_cpu.lua000066400000000000000000000064471265472057500232220ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/topprocs_errors.lua000066400000000000000000000031371265472057500237400ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/topprocs_file.lua000066400000000000000000000033221265472057500233370ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/topprocs_net.lua000066400000000000000000000031401265472057500232040ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/topscalls.lua000066400000000000000000000033241265472057500224750ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/topscalls_time.lua000066400000000000000000000032461265472057500235160ustar00rootroot00000000000000--[[ 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 compatable 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.8.0/userspace/sysdig/chisels/v_backlog.lua000066400000000000000000000040501265472057500224150ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/v_connections.lua000066400000000000000000000100311265472057500233310ustar00rootroot00000000000000--[[ 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"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "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.svc.id", "k8s.ns.id"}, 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, }, { name = "PROTO", description = "Connection protocol, obtained by resolving the server port name.", field = "fd.sproto", 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.8.0/userspace/sysdig/chisels/v_containers.lua000066400000000000000000000107441265472057500231670ustar00rootroot00000000000000--[[ 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."}, view_type = "table", applies_to = {"", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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 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.8.0/userspace/sysdig/chisels/v_containers_errors.lua000066400000000000000000000111161265472057500245550ustar00rootroot00000000000000--[[ 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.sport", "fd.sproto", "evt.type", "fd.directory", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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.8.0/userspace/sysdig/chisels/v_cpus.lua000066400000000000000000000031311265472057500217640ustar00rootroot00000000000000--[[ 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", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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.8.0/userspace/sysdig/chisels/v_directories.lua000066400000000000000000000061271265472057500233360ustar00rootroot00000000000000--[[ 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"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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.8.0/userspace/sysdig/chisels/v_errors.lua000066400000000000000000000036321265472057500223340ustar00rootroot00000000000000--[[ 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"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "fd.directory", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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.8.0/userspace/sysdig/chisels/v_file_opens.lua000066400000000000000000000044641265472057500231470ustar00rootroot00000000000000--[[ 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", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "fd.name", "fd.directory", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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.8.0/userspace/sysdig/chisels/v_files.lua000066400000000000000000000064311265472057500221220ustar00rootroot00000000000000--[[ 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"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "fd.directory", "fd.containerdirectory", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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.8.0/userspace/sysdig/chisels/v_incoming_connections.lua000066400000000000000000000034531265472057500252260ustar00rootroot00000000000000--[[ 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.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.svc.id", "k8s.ns.id"}, 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. 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 = "Name and argyuments of the process that received the connection.", colsize = 0 } } } sysdig-0.8.0/userspace/sysdig/chisels/v_io_by_type.lua000066400000000000000000000047711265472057500231670ustar00rootroot00000000000000--[[ 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"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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.8.0/userspace/sysdig/chisels/v_kubernetes_controllers.lua000066400000000000000000000060261265472057500256150ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/v_kubernetes_namespaces.lua000066400000000000000000000056451265472057500253740ustar00rootroot00000000000000--[[ 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.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.8.0/userspace/sysdig/chisels/v_kubernetes_pods.lua000066400000000000000000000066771265472057500242300ustar00rootroot00000000000000--[[ 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.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.8.0/userspace/sysdig/chisels/v_kubernetes_services.lua000066400000000000000000000057761265472057500251050ustar00rootroot00000000000000--[[ 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.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.8.0/userspace/sysdig/chisels/v_page_faults.lua000066400000000000000000000051401265472057500233060ustar00rootroot00000000000000--[[ 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"}, view_type = "table", filter = "evt.type!=switch", applies_to = {"", "container.id", "fd.name", "fd.sport", "fd.sproto", "evt.type", "fd.directory", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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.8.0/userspace/sysdig/chisels/v_procs.lua000066400000000000000000000076161265472057500221540ustar00rootroot00000000000000--[[ 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"}, view_type = "table", filter = "evt.type!=switch", applies_to = {"", "container.id", "fd.name", "fd.sport", "fd.sproto", "evt.type", "fd.directory", "fd.type", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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", }, }, } sysdig-0.8.0/userspace/sysdig/chisels/v_procs_cpu.lua000066400000000000000000000070071265472057500230150ustar00rootroot00000000000000--[[ 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." }, view_type = "table", filter = "evt.type!=switch", applies_to = {"", "container.id", "fd.name", "fd.sport", "fd.sproto", "evt.type", "fd.directory", "fd.type", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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 = "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.8.0/userspace/sysdig/chisels/v_procs_errors.lua000066400000000000000000000074561265472057500235520ustar00rootroot00000000000000--[[ 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"}, filter = "evt.type!=switch", view_type = "table", applies_to = {"", "container.id", "fd.name", "fd.sport", "fd.sproto", "evt.type", "fd.directory", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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.8.0/userspace/sysdig/chisels/v_procs_fd_usage.lua000066400000000000000000000070141265472057500240010ustar00rootroot00000000000000--[[ 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.sport", "fd.sproto", "evt.type", "fd.directory", "fd.type", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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.8.0/userspace/sysdig/chisels/v_spectro_all.lua000066400000000000000000000021601265472057500233220ustar00rootroot00000000000000--[[ 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 = "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.8.0/userspace/sysdig/chisels/v_spectro_file.lua000066400000000000000000000024611265472057500234750ustar00rootroot00000000000000--[[ 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 = "spectro_file", name = "Spectrogram-File", description = "File I/O latency spectrogram.", view_type = "spectrogram", applies_to = {"", "container.id", "proc.pid", "thread.tid", "proc.name", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id", "fd.name", "fd.containername", "fd.directory", "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.8.0/userspace/sysdig/chisels/v_sports.lua000066400000000000000000000044111265472057500223460ustar00rootroot00000000000000--[[ 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."}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "thread.tid", "proc.name", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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 = "PROTO", description = "protocol name, obtained by resolving the port number.", field = "fd.sproto", 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.8.0/userspace/sysdig/chisels/v_spy_syslog.lua000066400000000000000000000037231265472057500232340ustar00rootroot00000000000000--[[ 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", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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.8.0/userspace/sysdig/chisels/v_spy_users.lua000066400000000000000000000042171265472057500230540ustar00rootroot00000000000000--[[ 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.tid", "proc.name", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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.s", 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.8.0/userspace/sysdig/chisels/v_syscall_procs.lua000066400000000000000000000035371265472057500237040ustar00rootroot00000000000000--[[ 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.8.0/userspace/sysdig/chisels/v_syscalls.lua000066400000000000000000000043371265472057500226600ustar00rootroot00000000000000--[[ 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"}, view_type = "table", applies_to = {"", "container.id", "proc.pid", "proc.name", "thread.tid", "fd.sport", "fd.sproto", "fd.name", "fd.directory", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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.8.0/userspace/sysdig/chisels/v_threads.lua000066400000000000000000000057111265472057500224520ustar00rootroot00000000000000--[[ 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"}, view_type = "table", filter = "evt.type!=switch", applies_to = {"", "proc.pid", "proc.name", "container.id", "fd.sport", "fd.sproto", "fd.name", "fd.directory", "evt.res", "k8s.pod.id", "k8s.rc.id", "k8s.svc.id", "k8s.ns.id"}, 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 = "VTPID", 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.8.0/userspace/sysdig/config_sysdig.h.in000066400000000000000000000001641265472057500217400ustar00rootroot00000000000000#pragma once #define SYSDIG_VERSION "${SYSDIG_VERSION}" #define SYSDIG_INSTALLATION_DIR "${CMAKE_INSTALL_PREFIX}" sysdig-0.8.0/userspace/sysdig/csysdig.cpp000066400000000000000000000364471265472057500205210ustar00rootroot00000000000000/* 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-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" " -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" " -h, --help Print this page\n" " -k, --k8s-api 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 certificate file name to authenticate with the K8S API server.\n" " Filename must be a full absolute or relative (to the current directory) path\n" " to the certificate file.\n" " The certificate can also be specified via the environment variable.\n" " 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\n" " Don't convert port numbers to names.\n" " -n , --numevents=\n" " Stop capturing after events\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 , --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" " -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" "\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 to 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); } } } #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 m_raw_output = false; string* k8s_api = 0; string* k8s_api_cert = 0; bool xt1002_available = false; static struct option long_options[] = { {"delay", required_argument, 0, 'd' }, {"exclude-users", no_argument, 0, 'E' }, {"help", no_argument, 0, 'h' }, {"k8s-api", required_argument, 0, 'k'}, {"k8s-api-cert", required_argument, 0, 'K' }, {"list", optional_argument, 0, 'l' }, {"numevents", required_argument, 0, 'n' }, {"print", required_argument, 0, 'p' }, {"readfile", required_argument, 0, 'r' }, {"raw", no_argument, 0, 0 }, {"snaplen", required_argument, 0, 's' }, {"logfile", required_argument, 0, 0 }, {"view", required_argument, 0, 'v' }, {"version", no_argument, 0, 0 }, {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, "d:Ehk:K:lNn:p:r:s:v:", long_options, &long_index)) != -1) { switch(op) { case '?': // // Command line error // throw sinsp_exception("command line error"); 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 'l': list_flds = true; break; case 'N': inspector->set_hostname_and_port_resolution_mode(false); 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': infiles.push_back(optarg); k8s_api = new string(); break; case 's': snaplen = atoi(optarg); break; case 'v': display_view = optarg; 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 == "logfile") { inspector->set_log_file(optarg); } else if(optname == "raw") { m_raw_output = true; } } break; default: break; } } string filter; // // If -l was specified, print the fields and exit // if(list_flds) { list_fields(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; } // // Initialize ncurses // #ifndef NOCURSESUI if(!m_raw_output) { // // Check if xterm-1002 is available // xt1002_available =(tgetent(NULL, MOUSE_CAPABLE_TERM) != 0); if(xt1002_available) { // // Enable fine-grained mouse activity capture by setting xterm-1002 // setenv("TERM", MOUSE_CAPABLE_TERM, 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; } } view_manager.add(&it.m_viewinfo); } } // // Set the initial disply view // view_manager.set_selected_view(display_view); // // 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, m_raw_output, xt1002_available); ui.configure(&view_manager); ui.start(false, false); // // Launch the capture // bool open_success = true; 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) 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); } // // 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; } // // Start the capture loop // cinfo = do_inspect(inspector, cnt, &ui); // // Done. Close the capture. // inspector->close(); } } catch(sinsp_capture_interrupt_exception&) { } catch(sinsp_exception& e) { errorstr = e.what(); res.m_res = EXIT_FAILURE; } catch(...) { errorstr = "uncatched exception"; res.m_res = EXIT_FAILURE; } exit: if(inspector) { delete inspector; } // // Restore the original screen // #ifndef NOCURSESUI if(!m_raw_output) { 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.8.0/userspace/sysdig/fields_info.cpp000066400000000000000000000164511265472057500213260ustar00rootroot00000000000000/* 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) { uint32_t j, l, m; int32_t k; 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; } 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]; 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"); } } } const char* param_type_to_string(ppm_param_type pt) { switch(pt) { case PT_NONE: return "NONE"; break; case PT_INT8: return "INT8"; break; case PT_INT16: return "INT16"; break; case PT_INT32: return "INT32"; break; case PT_INT64: return "INT64"; break; case PT_UINT8: return "UINT8"; break; case PT_UINT16: return "UINT16"; break; case PT_UINT32: return "UINT32"; break; case PT_UINT64: return "UINT64"; break; case PT_CHARBUF: return "CHARBUF"; break; case PT_BYTEBUF: return "BYTEBUF"; break; case PT_ERRNO: return "ERRNO"; break; case PT_SOCKADDR: return "SOCKADDR"; break; case PT_SOCKTUPLE: return "SOCKTUPLE"; break; case PT_FD: return "FD"; break; case PT_PID: return "PID"; break; case PT_FDLIST: return "FDLIST"; break; case PT_FSPATH: return "FSPATH"; break; case PT_SYSCALLID: return "SYSCALLID"; break; case PT_SIGTYPE: return "SIGTYPE"; break; case PT_RELTIME: return "RELTIME"; break; case PT_ABSTIME: return "ABSTIME"; break; case PT_PORT: return "PORT"; break; case PT_L4PROTO: return "L4PROTO"; break; case PT_SOCKFAMILY: return "SOCKFAMILY"; break; case PT_BOOL: return "BOOL"; break; case PT_IPV4ADDR: return "IPV4ADDR"; break; case PT_DYN: return "DYNAMIC"; break; case PT_FLAGS8: return "FLAGS8"; break; case PT_FLAGS16: return "FLAGS16"; break; case PT_FLAGS32: return "FLAGS32"; break; case PT_UID: return "UID"; break; case PT_GID: return "GID"; break; case PT_SIGSET: return "SIGSET"; break; default: ASSERT(false); return ""; } } 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.8.0/userspace/sysdig/man/000077500000000000000000000000001265472057500171055ustar00rootroot00000000000000sysdig-0.8.0/userspace/sysdig/man/CMakeLists.txt000066400000000000000000000013251265472057500216460ustar00rootroot00000000000000find_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.8.0/userspace/sysdig/man/build.sh000077500000000000000000000001661265472057500205460ustar00rootroot00000000000000pandoc -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.8.0/userspace/sysdig/man/csysdig.8000066400000000000000000000242541265472057500206520ustar00rootroot00000000000000.\" Automatically generated by Pandoc 1.15.1.1 .\" .hy .TH "" "" "" "" "" .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 to 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 You can drill down multiple times, by keeping 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 selction 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 the columns with 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]\-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]filename\f[], \f[B]\-\-k8s\-api\-cert=\f[]\f[I]filename\f[] .PD 0 .P .PD Use the provided certificate file name to authenticate with the K8S API server. Filename must be a full absolute or relative (to the current directory) path to the certificate file. The certificate can also be specified 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]\-N\f[] .PD 0 .P .PD Don\[aq]t convert port numbers to names. .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]\-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[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]\-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.8.0/userspace/sysdig/man/csysdig.md000066400000000000000000000216361265472057500211040ustar00rootroot00000000000000NAME ---- 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 to 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_. You can drill down multiple times, by keeping 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 selction 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 the columns with 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. **-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** _filename_, **--k8s-api-cert=**_filename_ Use the provided certificate file name to authenticate with the K8S API server. Filename must be a full absolute or relative (to the current directory) path to the certificate file. The certificate can also be specified 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. **-N** Don't convert port numbers to names. **-n** _num_, **--numevents**=_num_ Stop capturing after _num_ events **-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** _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. **-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.8.0/userspace/sysdig/man/sysdig.8000066400000000000000000000337411265472057500205100ustar00rootroot00000000000000.\" Automatically generated by Pandoc 1.15.1.1 .\" .hy .TH "" "" "" "" "" .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]contains\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]filename\f[], \f[B]\-\-k8s\-api\-cert=\f[]\f[I]filename\f[] .PD 0 .P .PD Use the provided certificate file name to authenticate with the K8S API server. Filename must be a full absolute or relative (to the current directory) path to the certificate file. The certificate can also be specified 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]\-N\f[] .PD 0 .P .PD Don\[aq]t convert port numbers to names. .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]\-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. 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]\-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 beginning of the capture, \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]\-\-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 overwriten (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.8.0/userspace/sysdig/man/sysdig.md000066400000000000000000000300271265472057500207330ustar00rootroot00000000000000NAME ---- 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_, _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 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** _filename_, **--k8s-api-cert=**_filename_ Use the provided certificate file name to authenticate with the K8S API server. Filename must be a full absolute or relative (to the current directory) path to the certificate file. The certificate can also be specified 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. **-N** Don't convert port numbers to names. **-M** _num_seconds_ Stop collecting after reaching **-n** _num_, **--numevents**=_num_ Stop capturing after _num_ events **-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. 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_. **-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 beginning of the capture, **d** for delta between event enter and exit, and **D** for delta from the previous event. **--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 overwriten (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.8.0/userspace/sysdig/sysdig.cpp000066400000000000000000001151561265472057500203510ustar00rootroot00000000000000/* 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 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 certificate file name to authenticate with the K8S API server.\n" " Filename must be a full absolute or relative (to the current directory) path\n" " to the certificate file.\n" " The certificate can also be specified via the environment variable\n" " 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" " -M Stop collecting after reached.\n" " -N Don't convert port numbers to names.\n" " -n , --numevents=\n" " Stop capturing after events\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" " 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 , --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" " --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" "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 df(inspector, testflt); } 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(...) { } } // // Event processing loop // captureinfo do_inspect(sinsp* inspector, uint64_t cnt, int duration_to_tot, 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; int duration_start = 0; if(json) { do_flush = true; } // // Loop through the events // duration_start = ((double)clock()) / CLOCKS_PER_SEC; while(1) { if(duration_to_tot > 0) { int duration_tot = ((double)clock()) / CLOCKS_PER_SEC - duration_start; if(duration_tot >= duration_to_tot) { handle_end_of_file(print_progress, formatter); break; } } 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()); } 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 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; // 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' }, {"numevents", required_argument, 0, 'n' }, {"progress", required_argument, 0, 'P' }, {"print", required_argument, 0, 'p' }, {"quiet", no_argument, 0, 'q' }, {"readfile", required_argument, 0, 'r' }, {"snaplen", required_argument, 0, 's' }, {"summary", no_argument, 0, 'S' }, {"timetype", 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(); #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:Nn:Pp:qr:Ss:t:v" "W:" "w:xXz", long_options, &long_index)) != -1) { switch(op) { case 'A': if(event_buffer_format != sinsp_evt::PF_NORMAL) { fprintf(stderr, "you cannot specify more than one output format\n"); delete inspector; return sysdig_init_res(EXIT_SUCCESS); } event_buffer_format = sinsp_evt::PF_EOLS; break; case 'b': if(event_buffer_format != sinsp_evt::PF_NORMAL) { fprintf(stderr, "you cannot specify more than one output format\n"); delete inspector; return sysdig_init_res(EXIT_SUCCESS); } event_buffer_format = sinsp_evt::PF_BASE64; break; case 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_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': 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': inspector->set_hostname_and_port_resolution_mode(false); 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 { output_format = optarg; } break; case 'q': quiet = true; break; case 'r': infiles.push_back(optarg); k8s_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 '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 -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); } else { list_fields(false); } 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) { display_filter = new sinsp_filter(inspector, 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; } // // 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 // bool open_success = true; 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) 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(snaplen != 0) { inspector->set_snaplen(snaplen); } 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); 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; } cinfo = do_inspect(inspector, cnt, duration_to_tot, 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 = sinsp_split(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.8.0/userspace/sysdig/sysdig.h000066400000000000000000000036571265472057500200200ustar00rootroot00000000000000/* 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); const char* param_type_to_string(ppm_param_type pt); 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.8.0/userspace/sysdig/sysdig.vcxproj000066400000000000000000000120411265472057500212470ustar00rootroot00000000000000 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.8.0/userspace/sysdig/win32/000077500000000000000000000000001265472057500172745ustar00rootroot00000000000000sysdig-0.8.0/userspace/sysdig/win32/getopt.c000066400000000000000000000634131265472057500207510ustar00rootroot00000000000000/* 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.8.0/userspace/sysdig/win32/getopt.h000066400000000000000000000114461265472057500207550ustar00rootroot00000000000000/* 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.8.0/userspace/userspace.sln000066400000000000000000000041461265472057500175450ustar00rootroot00000000000000 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