pax_global_header00006660000000000000000000000064123705121550014513gustar00rootroot0000000000000052 comment=6e5eb28d3eabf95ff4c2fbd374f287cbde5d14b5 sysdig-0.1.87/000077500000000000000000000000001237051215500131125ustar00rootroot00000000000000sysdig-0.1.87/.gitignore000066400000000000000000000001061237051215500150770ustar00rootroot00000000000000*.o *.ko *.cmd *.symvers *.order *.mod *.mod.c build/ driver/Makefile sysdig-0.1.87/.travis.yml000066400000000000000000000027661237051215500152360ustar00rootroot00000000000000language: c env: - BUILD_TYPE=Debug NEW_CC=clang NEW_CXX=clang++ - BUILD_TYPE=Release NEW_CC=clang NEW_CXX=clang++ - BUILD_TYPE=Debug NEW_CC=gcc NEW_CXX=g++ - BUILD_TYPE=Release NEW_CC=gcc NEW_CXX=g++ - BUILD_TYPE=Debug NEW_CC=gcc-4.5 NEW_CXX=g++-4.5 - BUILD_TYPE=Release NEW_CC=gcc-4.5 NEW_CXX=g++-4.5 - BUILD_TYPE=Debug NEW_CC=gcc-4.4 NEW_CXX=g++-4.4 - BUILD_TYPE=Release NEW_CC=gcc-4.4 NEW_CXX=g++-4.4 before_install: - sudo apt-get update install: - sudo apt-get install rpm libluajit-5.1-dev libjsoncpp-dev linux-headers-generic gcc-4.5 g++-4.5 gcc-4.4 g++-4.4 before_script: - export KERNELDIR=/lib/modules/$(ls /lib/modules | sort | head -1)/build script: - export CC=$NEW_CC - export CXX=$NEW_CXX - mkdir build - cd build - cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE - make VERBOSE=1 - make package - cd .. - 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 - popd - rm -rf userspace/libsinsp/third-party/jsoncpp - mkdir build - cd build - cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DUSE_BUNDLED_JSONCPP=OFF -DUSE_BUNDLED_LUAJIT=OFF -DUSE_BUNDLED_ZLIB=OFF -DZLIB_PREFIX=/usr/local - make VERBOSE=1 - make package - cd .. - test/sysdig_trace_regression.sh build/userspace/sysdig/sysdig build/userspace/sysdig/chisels sysdig-0.1.87/CMakeCPackOptions.cmake000066400000000000000000000001451237051215500173520ustar00rootroot00000000000000if(CPACK_GENERATOR MATCHES "TGZ") set(CPACK_SET_DESTDIR "ON") set(CPACK_STRIP_FILES "OFF") endif() sysdig-0.1.87/CMakeLists.txt000066400000000000000000000212211237051215500156500ustar00rootroot00000000000000# 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.1-dev") 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() add_definitions(-DPLATFORM_NAME="${CMAKE_SYSTEM_NAME}") 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") add_subdirectory(driver) add_subdirectory(scripts) add_definitions(-DHAS_CAPTURE) endif() 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) # # LuaJIT # option(USE_BUNDLED_LUAJIT "Enable building of the bundled LuaJIT" ON) option(LUAJIT_PREFIX "Build with LuaJIT at the given path" "") if(LUAJIT_PREFIX AND USE_BUNDLED_LUAJIT) message(FATAL_ERROR "You must set either LUAJIT_PREFIX or USE_BUNDLED_LUAJIT " "not both.") endif() if(LUAJIT_PREFIX) find_path(LUAJIT_INCLUDE luajit.h "${LUAJIT_PREFIX}/*" NO_DEFAULT_PATH) find_library(LUAJIT_LIB NAMES luajit luajit-5.1 PATHS "${LUAJIT_PREFIX}/*" NO_DEFAULT_PATH) if(LUAJIT_INCLUDE AND LUAJIT_LIB) message(STATUS "Found LuaJIT: include: ${LUAJIT_INCLUDE}, lib: ${LUAJIT_LIB}") else() message(FATAL_ERROR "Couldn't find LuaJIT in '${LUAJIT_PREFIX}'") endif() elseif(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" ON) option(JSONCPP_PREFIX "Build with jsoncpp at the given path" "") if(JSONCPP_PREFIX AND USE_BUNDLED_JSONCPP) message(FATAL_ERROR "You must set either JSONCPP_PREFIX or USE_BUNDLED_JSONCPP " "not both.") endif() set(JSONCPP_SRC "${PROJECT_SOURCE_DIR}/userspace/libsinsp/third-party/jsoncpp") if(JSONCPP_PREFIX) find_path(JSONCPP_INCLUDE json/json.h "${JSONCPP_PREFIX}/*" NO_DEFAULT_PATH) find_library(JSONCPP_LIB NAMES jsoncpp PATHS "${JSONCPP_PREFIX}/*" NO_DEFAULT_PATH) if(JSONCPP_INCLUDE AND JSONCPP_LIB) message(STATUS "Found jsoncpp: include: ${JSONCPP_INCLUDE}, lib: ${JSONCPP_LIB}") else() message(FATAL_ERROR "Couldn't find jsoncpp in '${JSONCPP_PREFIX}'") endif() elseif(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" ON) option(ZLIB_PREFIX "Build with zlib at the given path" "") if(ZLIB_PREFIX AND USE_BUNDLED_ZLIB) message(FATAL_ERROR "You must set either ZLIB_PREFIX or USE_BUNDLED_ZLIB " "not both.") endif() if(ZLIB_PREFIX) find_path(ZLIB_INCLUDE zlib.h "${ZLIB_PREFIX}/*" NO_DEFAULT_PATH) find_library(ZLIB_LIB NAMES z PATHS "${ZLIB_PREFIX}/*" NO_DEFAULT_PATH) if(ZLIB_INCLUDE AND ZLIB_LIB) message(STATUS "Found zlib: include: ${ZLIB_INCLUDE}, lib: ${ZLIB_LIB}") else() message(FATAL_ERROR "Couldn't find zlib in '${ZLIB_PREFIX}'") endif() elseif(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() add_subdirectory(userspace/sysdig) add_subdirectory(userspace/libscap) add_subdirectory(userspace/libsinsp) set(CPACK_PACKAGE_NAME "sysdig") set(CPACK_PACKAGE_VENDOR "Draios Inc.") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A system 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 "Draios Inc. ") set(CPACK_DEBIAN_PACKAGE_SECTION "utils") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://www.draios.com") 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.draios.com") 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) include(CPack) sysdig-0.1.87/COPYING000066400000000000000000000432541237051215500141550ustar00rootroot00000000000000 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.1.87/README.md000066400000000000000000000034531237051215500143760ustar00rootroot00000000000000![](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) Welcome to **sysdig** - an open source system-level exploration and troubleshooting tool. 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 captures system calls and other system level events using a linux kernel facility called tracepoints, providing a rich set of real-time, system-level information. Sysdig "packetizes" this information, so that you can do things like save it into trace files and easily filter it, a bit like you would do with tcpdump. This makes it very flexible to explore what processes are doing. Sysdig is also packed with a set of scripts called Chisels that make it easier to extract useful information and do troubleshooting. Sysdig is designed from the ground up for minimal overhead and is production ready. 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] (http://draios.com/blog/). There are many like it, but this one is ours. Draios --- Sysdig is proudly supported by [Draios, Inc] (http://www.draios.com). Like working on sysdig? [Draios is hiring] (http://www.draios.com/about+jobs). sysdig-0.1.87/coding_conventions.md000066400000000000000000000142471237051215500173340ustar00rootroot000000000000000 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.1.87/common/000077500000000000000000000000001237051215500144025ustar00rootroot00000000000000sysdig-0.1.87/common/inttypes_win.h000066400000000000000000000173021237051215500173120ustar00rootroot00000000000000// 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.1.87/driver/000077500000000000000000000000001237051215500144055ustar00rootroot00000000000000sysdig-0.1.87/driver/CMakeLists.txt000066400000000000000000000031471237051215500171520ustar00rootroot00000000000000option(BUILD_DRIVER "Build the driver on Linux" ON) if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(KBUILD_FLAGS "${SYSDIG_DEBUG_FLAGS} ${SYSDIG_FEATURE_FLAGS}") else() set(KBUILD_FLAGS "${SYSDIG_FEATURE_FLAGS}") endif() configure_file(dkms.conf.in dkms.conf) configure_file(Makefile.in Makefile.dkms) set(CLEAN_FILES "${CMAKE_CURRENT_SOURCE_DIR}/Makefile") set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${CLEAN_FILES}") # 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 cp -f ${CMAKE_CURRENT_BINARY_DIR}/Makefile.dkms Makefile COMMAND $(MAKE) COMMAND cp -f sysdig-probe.ko "${CMAKE_CURRENT_BINARY_DIR}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" VERBATIM) else() add_custom_target(driver COMMAND cp -f ${CMAKE_CURRENT_BINARY_DIR}/Makefile.dkms Makefile COMMAND $(MAKE) COMMAND cp -f sysdig-probe.ko "${CMAKE_CURRENT_BINARY_DIR}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" VERBATIM) endif() add_custom_target(install_driver COMMAND $(MAKE) install DEPENDS driver WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" VERBATIM) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Makefile.dkms RENAME Makefile DESTINATION "src/sysdig-${SYSDIG_VERSION}") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dkms.conf dynamic_params_table.c 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 syscall_table.c DESTINATION "src/sysdig-${SYSDIG_VERSION}") sysdig-0.1.87/driver/Makefile.in000066400000000000000000000006241237051215500164540ustar00rootroot00000000000000sysdig-probe-y += main.o dynamic_params_table.o flags_table.o ppm_events.o ppm_fillers.o event_table.o syscall_table.o obj-m += sysdig-probe.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.1.87/driver/dkms.conf.in000066400000000000000000000002271237051215500166200ustar00rootroot00000000000000PACKAGE_NAME="sysdig" PACKAGE_VERSION="@SYSDIG_VERSION@" BUILT_MODULE_NAME[0]="sysdig-probe" DEST_MODULE_LOCATION[0]="/kernel/extra" AUTOINSTALL="yes" sysdig-0.1.87/driver/dynamic_params_table.c000066400000000000000000000014661237051215500207160ustar00rootroot00000000000000/* 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.1.87/driver/event_table.c000066400000000000000000000604561237051215500170540ustar00rootroot00000000000000/* 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_NONE, 1, {{"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_BRK_1_X */{"brk", EC_MEMORY, EF_NONE, 1, {{"res", PT_UINT64, PF_HEX} } }, /* PPME_SYSCALL_EXECVE_8_E */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_EXECVE_8_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 8, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC} } }, /* PPME_CLONE_11_E */{"clone", EC_PROCESS, EF_MODIFIES_STATE, 0}, /* PPME_CLONE_11_X */{"clone", EC_PROCESS, EF_MODIFIES_STATE, 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, 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), 0}, /* PPME_SOCKET_ACCEPT_X */{"accept", EC_NET, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 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), 1, {{"flags", PT_INT32, PF_HEX} } }, /* PPME_SOCKET_ACCEPT4_X */{"accept", EC_NET, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 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_E */{"ioctl", EC_IO_OTHER, EF_USES_FD, 2, {{"fd", PT_FD, PF_DEC}, {"request", PT_UINT64, PF_HEX} } }, /* PPME_SYSCALL_IOCTL_X */{"ioctl", EC_IO_OTHER, EF_USES_FD, 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, EF_NONE, 1, {{"next", PT_PID, PF_DEC} } }, /* PPME_SCHEDSWITCH_1_X */{"NA2", EC_SCHEDULER, EF_UNUSED, 0}, /* PPME_DROP_E */{"drop", EC_INTERNAL, EF_NONE, 1, {{"ratio", PT_UINT32, PF_DEC} } }, /* PPME_DROP_X */{"drop", EC_INTERNAL, EF_NONE, 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, EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_EXECVE_13_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 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, 0}, /* PPME_CLONE_16_X */{"clone", EC_PROCESS, EF_MODIFIES_STATE, 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} } }, }; sysdig-0.1.87/driver/flags_table.c000066400000000000000000000217321237051215500170210ustar00rootroot00000000000000/* 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_NONE", PPM_O_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}, { }, }; 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}, { }, }; 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}, { }, }; sysdig-0.1.87/driver/main.c000066400000000000000000001047021237051215500155010ustar00rootroot00000000000000/* 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, 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" MODULE_LICENSE("GPL"); MODULE_AUTHOR("Draios"); #define PPM_DEVICE_NAME "sysdig" #define PPE_DEVICE_NAME PPM_DEVICE_NAME "-events" #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; }; /* * The ring descriptor. * We have one of these for each CPU. */ struct ppm_ring_buffer_context { 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. */ }; /* * 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 void record_event(enum ppm_event_type event_type, struct pt_regs *regs, long id, int never_drop, struct task_struct *sched_prev, struct task_struct *sched_next); static ssize_t ppe_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos); #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); #else TRACEPOINT_PROBE(sched_switch_probe, struct task_struct *prev, struct task_struct *next); #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) */ #endif /* CAPTURE_CONTEXT_SWITCHES */ 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 */ static DEFINE_PER_CPU(struct ppm_ring_buffer_context*, g_ring_buffers); static DEFINE_MUTEX(g_open_mutex); static u32 g_open_count; u32 g_snaplen = RW_SNAPLEN; u32 g_sampling_ratio = 1; static u32 g_sampling_interval; static int g_is_dropping; static int g_dropping_mode; static bool g_tracepoint_registered; struct cdev *g_ppe_cdev = NULL; struct device *g_ppe_dev = NULL; static struct tracepoint *tp_sys_enter; static struct tracepoint *tp_sys_exit; static struct tracepoint *tp_sched_process_exit; #ifdef CAPTURE_CONTEXT_SWITCHES static struct tracepoint *tp_sched_switch; #endif #ifdef _DEBUG static bool verbose = 1; #else static bool verbose = 0; #endif #define vpr_info(fmt, ...) \ do { \ if (verbose) \ pr_info(fmt, ##__VA_ARGS__); \ } while (0) /* compat tracepoint functions */ static int compat_register_trace(void *func, const char *probename, struct tracepoint *tp) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)) return TRACEPOINT_PROBE_REGISTER(probename, func); #else return tracepoint_probe_register(tp, func, NULL); #endif } static void compat_unregister_trace(void *func, const char *probename, struct tracepoint *tp) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)) TRACEPOINT_PROBE_UNREGISTER(probename, func); #else tracepoint_probe_unregister(tp, func, NULL); #endif } /* * user I/O functions */ static int ppm_open(struct inode *inode, struct file *filp) { int ret; struct ppm_ring_buffer_context *ring; int ring_no = iminor(filp->f_dentry->d_inode); mutex_lock(&g_open_mutex); ring = per_cpu(g_ring_buffers, ring_no); if (ring->open) { pr_err("invalid operation: attempting to open device %d multiple times\n", ring_no); ret = -EBUSY; goto cleanup_open; } vpr_info("opening ring %d\n", ring_no); /* * 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. */ g_dropping_mode = 0; g_snaplen = RW_SNAPLEN; g_sampling_ratio = 1; g_sampling_interval = 0; g_is_dropping = 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 = true; getnstimeofday(&ring->last_print_time); ring->open = true; if (!g_tracepoint_registered) { vpr_info("starting capture\n"); /* * Enable the tracepoints */ ret = compat_register_trace(syscall_exit_probe, "sys_exit", tp_sys_exit); if (ret) { pr_err("can't create the sys_exit tracepoint\n"); goto err_sys_exit; } ret = compat_register_trace(syscall_enter_probe, "sys_enter", tp_sys_enter); 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 g_tracepoint_registered = true; } ++g_open_count; ret = 0; goto cleanup_open; err_sched_switch: compat_unregister_trace(syscall_procexit_probe, "sched_process_exit", tp_sched_process_exit); err_sched_procexit: compat_unregister_trace(syscall_enter_probe, "sys_enter", tp_sys_enter); err_sys_enter: compat_unregister_trace(syscall_exit_probe, "sys_exit", tp_sys_exit); err_sys_exit: ring->open = false; cleanup_open: mutex_unlock(&g_open_mutex); return ret; } static int ppm_release(struct inode *inode, struct file *filp) { int ret; struct ppm_ring_buffer_context *ring; int ring_no = iminor(filp->f_dentry->d_inode); mutex_lock(&g_open_mutex); ring = per_cpu(g_ring_buffers, ring_no); if (!ring->open) { pr_err("attempting to close unopened device %d\n", ring_no); ret = -EBUSY; goto cleanup_release; } ring->capture_enabled = false; vpr_info("closing ring %d, evt:%llu, dr_buf:%llu, dr_pf:%llu, pr:%llu, cs:%llu\n", ring_no, ring->info->n_evts, ring->info->n_drops_buffer, ring->info->n_drops_pf, ring->info->n_preemptions, ring->info->n_context_switches); /* * The last closed device stops event collection */ --g_open_count; if (g_open_count == 0) { if (g_tracepoint_registered) { vpr_info("stopping capture\n"); compat_unregister_trace(syscall_exit_probe, "sys_exit", tp_sys_exit); compat_unregister_trace(syscall_enter_probe, "sys_enter", tp_sys_enter); 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 tracepoint_synchronize_unregister(); g_tracepoint_registered = false; } else { ASSERT(false); } } ring->open = false; ret = 0; cleanup_release: mutex_unlock(&g_open_mutex); return ret; } static long ppm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { switch (cmd) { case PPM_IOCTL_DISABLE_CAPTURE: { int ring_no = iminor(filp->f_dentry->d_inode); struct ppm_ring_buffer_context *ring = per_cpu(g_ring_buffers, ring_no); mutex_lock(&g_open_mutex); ring->capture_enabled = false; mutex_unlock(&g_open_mutex); vpr_info("PPM_IOCTL_DISABLE_CAPTURE for ring %d\n", ring_no); return 0; } case PPM_IOCTL_ENABLE_CAPTURE: { int ring_no = iminor(filp->f_dentry->d_inode); struct ppm_ring_buffer_context *ring = per_cpu(g_ring_buffers, ring_no); mutex_lock(&g_open_mutex); ring->capture_enabled = true; mutex_unlock(&g_open_mutex); vpr_info("PPM_IOCTL_ENABLE_CAPTURE for ring %d\n", ring_no); return 0; } case PPM_IOCTL_DISABLE_DROPPING_MODE: { g_dropping_mode = 0; vpr_info("PPM_IOCTL_DISABLE_DROPPING_MODE\n"); g_sampling_interval = 1000000000; g_sampling_ratio = 1; return 0; } case PPM_IOCTL_ENABLE_DROPPING_MODE: { u32 new_sampling_ratio; g_dropping_mode = 1; vpr_info("PPM_IOCTL_ENABLE_DROPPING_MODE\n"); 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; } g_sampling_interval = 1000000000 / new_sampling_ratio; g_sampling_ratio = new_sampling_ratio; vpr_info("new sampling ratio: %d\n", new_sampling_ratio); return 0; } case PPM_IOCTL_SET_SNAPLEN: { u32 new_snaplen; vpr_info("PPM_IOCTL_SET_SNAPLEN\n"); new_snaplen = (u32)arg; if (new_snaplen > RW_MAX_SNAPLEN) { pr_err("invalid snaplen %u\n", new_snaplen); return -EINVAL; } g_snaplen = new_snaplen; vpr_info("new snaplen: %d\n", g_snaplen); return 0; } case PPM_IOCTL_MASK_ZERO_EVENTS: { vpr_info("PPM_IOCTL_MASK_ZERO_EVENTS\n"); 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); return 0; } case PPM_IOCTL_MASK_SET_EVENT: { u32 syscall_to_set = (u32)arg; vpr_info("PPM_IOCTL_MASK_SET_EVENT (%u)\n", syscall_to_set); 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); return 0; } case PPM_IOCTL_MASK_UNSET_EVENT: { u32 syscall_to_unset = (u32)arg; vpr_info("PPM_IOCTL_MASK_UNSET_EVENT (%u)\n", syscall_to_unset); 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); return 0; } default: return -ENOTTY; } } static int ppm_mmap(struct file *filp, struct vm_area_struct *vma) { if (vma->vm_pgoff == 0) { int ret; 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; int ring_no = iminor(filp->f_dentry->d_inode); struct ppm_ring_buffer_context *ring; vpr_info("mmap for CPU %d, start=%lu len=%ld page_size=%lu\n", 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); return -EIO; } 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"); return -EIO; } /* * Retrieve the ring structure for this CPU */ ring = per_cpu(g_ring_buffers, ring_no); 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"); return ret; } return 0; } 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); return -EIO; } /* * 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"); return ret; } 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"); return ret; } useraddr += PAGE_SIZE; vmalloc_area_ptr += PAGE_SIZE; mlength -= PAGE_SIZE; } return 0; } else { pr_err("Invalid mmap size %ld\n", length); return -EIO; } } pr_err("invalid pgoff %lu, must be 0\n", vma->vm_pgoff); return -EIO; } 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 __NR_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; if (unlikely(ppm_copy_from_user(filler_args->socketcall_args, scargs, nas[socketcall_id]))) return PPME_GENERIC_E; 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_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_E; default: ASSERT(false); return PPME_GENERIC_E; } } #endif /* __NR_socketcall */ static inline int drop_event(enum ppm_event_type event_type, int never_drop, struct timespec *ts) { if (never_drop) return 0; if (g_dropping_mode) { if (ts->tv_nsec >= g_sampling_interval) { if (g_is_dropping == 0) { g_is_dropping = 1; record_event(PPME_DROP_E, NULL, -1, 1, NULL, NULL); } return 1; } else { if (g_is_dropping == 1) { g_is_dropping = 0; record_event(PPME_DROP_X, NULL, -1, 1, NULL, NULL); } } } return 0; } static void record_event(enum ppm_event_type event_type, struct pt_regs *regs, long id, int never_drop, struct task_struct *sched_prev, struct task_struct *sched_next) { size_t event_size; int next; u32 freespace; u32 usedspace; 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; struct timespec ts; getnstimeofday(&ts); if (!test_bit(event_type, g_events_mask)) return; if (drop_event(event_type, never_drop, &ts)) return; /* * FROM THIS MOMENT ON, WE HAVE TO BE SUPER FAST */ ring = get_cpu_var(g_ring_buffers); ring_info = ring->info; if (!ring->capture_enabled) { put_cpu_var(g_ring_buffers); return; } ring_info->n_evts++; if (sched_prev != NULL) { ASSERT(sched_prev != NULL); ASSERT(sched_next != NULL); ASSERT(regs == NULL); ring_info->n_context_switches++; } /* * Preemption gate */ if (unlikely(atomic_inc_return(&ring->preempt_count) != 1)) { atomic_dec(&ring->preempt_count); put_cpu_var(g_ring_buffers); ring_info->n_preemptions++; ASSERT(false); return; } /* * 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; ASSERT(freespace <= RING_BUF_SIZE); ASSERT(usedspace <= RING_BUF_SIZE); ASSERT(ttail <= RING_BUF_SIZE); ASSERT(head <= RING_BUF_SIZE); #ifdef __NR_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 (regs && id == __NR_socketcall) { enum ppm_event_type tet; tet = parse_socketcall(&args, regs); if (event_type == PPME_GENERIC_E) event_type = tet; else event_type = tet + 1; } #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.buffer = ring->buffer + head + sizeof(struct ppm_evt_hdr); #ifdef PPM_ENABLE_SENTINEL args.sentinel = ring->nevents; #endif args.buffer_size = min(freespace, (u32)(2 * PAGE_SIZE)) - sizeof(struct ppm_evt_hdr); /* freespace is guaranteed to be bigger than sizeof(struct ppm_evt_hdr) */ args.event_type = event_type; args.regs = regs; args.sched_prev = sched_prev; args.sched_next = sched_next; args.syscall_id = id; args.curarg = 0; args.arg_data_size = args.buffer_size - args.arg_data_offset; args.nevents = ring->nevents; args.str_storage = ring->str_storage; /* * 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)) { 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)) { 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); } } #ifdef _DEBUG if (ts.tv_sec > ring->last_print_time.tv_sec + 1) { vpr_info("CPU%d, use:%d%%, ev:%llu, dr_buf:%llu, dr_pf:%llu, pr:%llu, cs:%llu\n", 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; } #endif atomic_dec(&ring->preempt_count); put_cpu_var(g_ring_buffers); return; } TRACEPOINT_PROBE(syscall_enter_probe, struct pt_regs *regs, long id) { long table_index; #ifdef CONFIG_X86_64 /* * If this is a 32bit process running on a 64bit kernel (see the CONFIG_IA32_EMULATION * kernel flag), we skip its events. * XXX Decide what to do about this. */ if (unlikely(test_tsk_thread_flag(current, TIF_IA32))) return; #endif table_index = id - SYSCALL_TABLE_ID0; if (likely(table_index >= 0 && table_index < SYSCALL_TABLE_SIZE)) { int used = g_syscall_table[table_index].flags & UF_USED; int never_drop = g_syscall_table[table_index].flags & UF_NEVER_DROP; if (used) record_event(g_syscall_table[table_index].enter_event_type, regs, id, never_drop, NULL, NULL); else record_event(PPME_GENERIC_E, regs, id, false, NULL, NULL); } } TRACEPOINT_PROBE(syscall_exit_probe, struct pt_regs *regs, long ret) { int id; long table_index; #ifdef CONFIG_X86_64 /* * If this is a 32bit process running on a 64bit kernel (see the CONFIG_IA32_EMULATION * kernel flag), we skip its events. * XXX Decide what to do about this. */ if (unlikely(test_tsk_thread_flag(current, TIF_IA32))) return; #endif id = syscall_get_nr(current, regs); table_index = id - SYSCALL_TABLE_ID0; if (likely(table_index >= 0 && table_index < SYSCALL_TABLE_SIZE)) { int used = g_syscall_table[table_index].flags & UF_USED; int never_drop = g_syscall_table[table_index].flags & UF_NEVER_DROP; if (used) record_event(g_syscall_table[table_index].exit_event_type, regs, id, never_drop, NULL, NULL); else record_event(PPME_GENERIC_X, regs, id, false, NULL, NULL); } } 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) { if (unlikely(current->flags & PF_KTHREAD)) { /* * We are not interested in kernel threads */ return; } record_event(PPME_PROCEXIT_E, NULL, -1, 1, NULL, NULL); } #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) #else TRACEPOINT_PROBE(sched_switch_probe, struct task_struct *prev, struct task_struct *next) #endif { record_event(PPME_SCHEDSWITCH_6_E, NULL, -1, 0, prev, next); } #endif static struct ppm_ring_buffer_context *alloc_ring_buffer(struct ppm_ring_buffer_context **ring) { unsigned int j; /* * Allocate the ring descriptor */ *ring = vmalloc(sizeof(struct ppm_ring_buffer_context)); if (*ring == NULL) { pr_err("Error allocating ring memory\n"); return NULL; } /* * 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 *ring; err_ring_info: vfree((void *)(*ring)->buffer); err_buffer: free_page((unsigned long)(*ring)->str_storage); err_str_storage: vfree(*ring); return NULL; } static void free_ring_buffer(struct ppm_ring_buffer_context *ring) { vfree(ring->info); vfree((void *)ring->buffer); free_page((unsigned long)ring->str_storage); vfree(ring); } #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 } 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 return 0; } #else static int get_tracepoint_handles(void) { return 0; } #endif #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; } 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; struct device *device = NULL; pr_info("driver loading\n"); ret = get_tracepoint_handles(); if (ret < 0) goto init_module_err; /* * Initialize the ring buffers array */ num_cpus = 0; for_each_online_cpu(cpu) { per_cpu(g_ring_buffers, cpu) = NULL; ++num_cpus; } for_each_online_cpu(cpu) { pr_info("initializing ring buffer for CPU %u\n", cpu); alloc_ring_buffer(&per_cpu(g_ring_buffers, cpu)); if (per_cpu(g_ring_buffers, cpu) == NULL) { pr_err("can't initialize the ring buffer for CPU %u\n", cpu); ret = -ENOMEM; goto init_module_err; } } /* * Initialize the user I/O * ( + 1 for sysdig-events) */ acrret = alloc_chrdev_region(&dev, 0, num_cpus + 1, PPM_DEVICE_NAME); if (acrret < 0) { pr_err("could not allocate major number for %s\n", PPM_DEVICE_NAME); ret = -ENOMEM; goto init_module_err; } g_ppm_class = class_create(THIS_MODULE, PPM_DEVICE_NAME); if (IS_ERR(g_ppm_class)) { pr_err("can't allocate device class\n"); ret = -EFAULT; goto init_module_err; } g_ppm_class->devnode = ppm_devnode; g_ppm_major = MAJOR(dev); g_ppm_numdevs = num_cpus; g_ppm_devs = kmalloc(g_ppm_numdevs * sizeof(struct ppm_device), GFP_KERNEL); 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", PPM_DEVICE_NAME); ret = -EFAULT; goto init_module_err; } device = device_create(g_ppm_class, NULL, /* no parent device */ g_ppm_devs[j].dev, NULL, /* no additional data */ PPM_DEVICE_NAME "%d", j); if (IS_ERR(device)) { pr_err("error creating the device for %s\n", PPM_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", PPE_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", PPE_DEVICE_NAME); ret = -EFAULT; goto init_module_err; } g_ppe_dev = device_create(g_ppm_class, NULL, MKDEV(g_ppm_major, g_ppm_numdevs), NULL, /* no additional data */ PPE_DEVICE_NAME); if (IS_ERR(g_ppe_dev)) { pr_err("error creating the device for %s\n", PPE_DEVICE_NAME); ret = -EFAULT; goto init_module_err; } /* * All ok. Final initalizations. */ g_open_count = 0; g_tracepoint_registered = false; g_dropping_mode = 0; return 0; init_module_err: for_each_online_cpu(cpu) if (per_cpu(g_ring_buffers, cpu) != NULL) free_ring_buffer(per_cpu(g_ring_buffers, cpu)); /* remove_proc_entry(PPM_DEVICE_NAME, NULL); */ 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; int cpu; pr_info("driver unloading\n"); /* remove_proc_entry(PPM_DEVICE_NAME, NULL); */ for_each_online_cpu(cpu) free_ring_buffer(per_cpu(g_ring_buffers, cpu)); 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); tracepoint_synchronize_unregister(); } module_init(sysdig_init); module_exit(sysdig_exit); module_param(verbose, bool, 0); sysdig-0.1.87/driver/ppm.h000066400000000000000000000037011237051215500153530ustar00rootroot00000000000000/* 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 . */ /* * 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 /* * Global defines */ #define CAPTURE_CONTEXT_SWITCHES #define RW_SNAPLEN 80 #define RW_SNAPLEN_EVENT 4096 #define RW_MAX_SNAPLEN (256 * 1024 * 1024) /* Make sure to use a power of two constant for this */ extern u32 g_snaplen; /* * Global enums */ enum syscall_flags { UF_NONE = 0, UF_USED = (1 << 0), UF_NEVER_DROP = (1 << 1), }; /* * Global structs */ struct syscall_evt_pair { int flags; enum ppm_event_type enter_event_type; enum ppm_event_type exit_event_type; }; #define STR_STORAGE_SIZE PAGE_SIZE /* * Global functions */ 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[]; extern u32 g_sampling_ratio; sysdig-0.1.87/driver/ppm_events.c000066400000000000000000000576321237051215500167460ustar00rootroot00000000000000/* 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 "ppm_ringbuffer.h" #include "ppm_events_public.h" #include "ppm_events.h" #include "ppm.h" /* * do-nothing implementation of compat_ptr for systems that are not compiled * with CONFIG_COMPAT. */ #ifndef CONFIG_COMPAT #define compat_ptr(X) X #endif /* * 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]); } /* * 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, n)) { 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; } /* * 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 */ inline 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); ++len; } /* * 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); ++len; } break; case PT_BYTEBUF: 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; } else { 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: 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. */ 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; } 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; } else if (family == AF_RDS) { return PPM_AF_RDS; } 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; } else if (family == AF_CAN) { return PPM_AF_CAN; } 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; } else if (family == AF_RXRPC) { return PPM_AF_RXRPC; } else if (family == AF_ISDN) { return PPM_AF_ISDN; } else if (family == AF_PHONET) { return PPM_AF_PHONET; } else if (family == AF_IEEE802154) { return PPM_AF_IEEE802154; } #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); ASSERT(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 { /* * 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; uint64_t size = 0; unsigned long bufsize; char *targetbuf = args->str_storage; copylen = iovcnt * sizeof(struct iovec); if (unlikely(copylen >= STR_STORAGE_SIZE)) return PPM_FAILURE_BUFFER_FULL; if (unlikely(ppm_copy_from_user(targetbuf, iovsrc, copylen))) return PPM_FAILURE_INVALID_USER_MEMORY; iov = (const struct iovec *)targetbuf; /* * Size */ if (flags & PRB_FLAG_PUSH_SIZE) { for (j = 0; j < iovcnt; j++) size += iov[j].iov_len; res = val_to_ring(args, size, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } /* * data * NOTE: for the moment, we limit our data copy to the first buffer. * We assume that in the vast majority of the cases g_snaplen is much smaller * than iov[0].iov_len, and therefore we don't bother complicvating the code. */ if (flags & PRB_FLAG_PUSH_DATA) { if (retval > 0 && iovcnt > 0) { bufsize = min_t(int64_t, retval, (int64_t)iov[0].iov_len); res = val_to_ring(args, (unsigned long)iov[0].iov_base, min(bufsize, (unsigned long)g_snaplen), true, 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; } /* * 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) { #ifndef __NR_socketcall /* * Regular argument */ syscall_get_arguments(current, args->regs, evinfo->autofill_args[j].id, 1, &val); #else if (evinfo->paramtype == APT_SOCK) { val = args->socketcall_args[evinfo->autofill_args[j].id]; } else { /* * Regular argument */ syscall_get_arguments(current, args->regs, evinfo->autofill_args[j].id, 1, &val); } #endif 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.1.87/driver/ppm_events.h000066400000000000000000000070251237051215500167420ustar00rootroot00000000000000/* 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 /* * Various crap that a callback might need */ struct event_filler_arguments { 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 */ #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. */ #ifdef __NR_socketcall unsigned long socketcall_args[6]; #endif }; /* * 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) /* * Functions */ 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, uint64_t val, u16 val_len, bool fromuser, uint8_t 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); 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; } else { return PPM_FAILURE_BUFFER_FULL; } #else return PPM_SUCCESS; #endif } #endif /* EVENTS_H_ */ sysdig-0.1.87/driver/ppm_events_public.h000066400000000000000000000756761237051215500203210ustar00rootroot00000000000000/* 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 16 /* 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) /* * 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. */ /* * 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) /* * 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) /* * 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)) /** @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_CLONE_11_E = 14, PPME_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_E = 90, PPME_SYSCALL_IOCTL_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_CLONE_16_E = 156, PPME_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, PPM_EVENT_MAX = 170 }; /*@}*/ /* * 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_MAX = 303, }; /* * 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, /* General wait (can be file, socket, IPC...) */ 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 no */ EF_WAITS = (1 << 7), /* This event reads data from an FD. */ }; /* * 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, }; /* * 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. */ }; 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 */ }; /*! \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) /*! \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 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 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_param_info ptrace_dynamic_param[]; #endif /* EVENTS_PUBLIC_H_ */ sysdig-0.1.87/driver/ppm_fillers.c000066400000000000000000002401461237051215500170740ustar00rootroot00000000000000/* 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 "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); /* * 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_E] = {f_sys_empty}, [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_E] = {f_sys_empty}, [PPME_SOCKET_ACCEPT_X] = {f_sys_accept_x}, [PPME_SOCKET_ACCEPT4_E] = {f_sys_accept4_e}, [PPME_SOCKET_ACCEPT4_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_E] = {PPM_AUTOFILL, 2, APT_REG, {{0}, {1} } }, [PPME_SYSCALL_IOCTL_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_13_E] = {f_sys_empty}, [PPME_SYSCALL_EXECVE_13_X] = {f_proc_startupdate}, [PPME_CLONE_16_E] = {f_sys_empty}, [PPME_CLONE_16_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}, }; /* * do-nothing implementation of compat_ptr for systems that are not compiled * with CONFIG_COMPAT. */ #ifndef CONFIG_COMPAT #define compat_ptr(X) X #endif #define merge_64(hi, lo) ((((unsigned long long)(hi)) << 32) + ((lo) & 0xffffffffUL)); static int f_sys_generic(struct event_filler_arguments *args) { int res; #ifdef __NR_socketcall if (unlikely(args->syscall_id == __NR_socketcall)) { /* * All the socket calls should be implemented */ ASSERT(false); return PPM_FAILURE_BUG; } else { #endif /* __NR_socketcall */ /* * name */ long table_index = args->syscall_id - SYSCALL_TABLE_ID0; if (likely(table_index >= 0 && table_index < SYSCALL_TABLE_SIZE)) { enum ppm_syscall_code sc_code = 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; } #ifdef __NR_socketcall } #endif 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; 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; unsigned int snaplen; /* * 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; } /* * Determine the snaplen by checking the fd type. * (note: not implemeted yet) */ snaplen = g_snaplen; #if 0 { int fd; int err, fput_needed; struct socket *sock; syscall_get_arguments(current, args->regs, 0, 1, &val); fd = (int)val; sock = ppm_sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { snaplen = g_snaplen; fput_light(sock->file, fput_needed); } else { snaplen = RW_SNAPLEN; } } #endif /* * Copy the buffer */ res = val_to_ring(args, val, min_t(unsigned long, bufsize, (unsigned long)snaplen), 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; unsigned int snaplen; /* * If the write event is directed to our sysdig-events device, we use a * bigger snaplen */ snaplen = g_snaplen; { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) int fd; struct fd f; syscall_get_arguments(current, args->regs, 0, 1, &val); fd = (int)val; f = fdget(fd); if (f.file && f.file->f_op) { if (THIS_MODULE == f.file->f_op->owner) snaplen = RW_SNAPLEN_EVENT; fdput(f); } #else int fd; struct file *file; syscall_get_arguments(current, args->regs, 0, 1, &val); fd = (int)val; file = fget(fd); if (file && file->f_op) { if (THIS_MODULE == file->f_op->owner) snaplen = RW_SNAPLEN_EVENT; fput(file); } #endif } /* * 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); res = val_to_ring(args, val, min_t(unsigned long, bufsize, (unsigned long)snaplen), 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; if (flags & CLONE_IO) res |= PPM_CL_CLONE_IO; if (flags & CLONE_NEWIPC) res |= PPM_CL_CLONE_NEWIPC; if (flags & CLONE_NEWNET) res |= PPM_CL_CLONE_NEWNET; if (flags & CLONE_NEWNS) res |= PPM_CL_CLONE_NEWNS; if (flags & CLONE_NEWPID) res |= PPM_CL_CLONE_NEWPID; if (flags & CLONE_NEWUTS) res |= PPM_CL_CLONE_NEWUTS; 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; 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; } static int f_proc_startupdate(struct event_filler_arguments *args) { unsigned long val; int res = 0; unsigned int exe_len = 0; unsigned int args_len = 0; struct mm_struct *mm = current->mm; int64_t retval; const char *argstr; int ptid; char *spwd; long total_vm = 0; long total_rss = 0; long swap = 0; /* * 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 (likely(retval >= 0)) { 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 > 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; exe_len = strnlen(args->str_storage, args_len); if (exe_len < args_len) ++exe_len; } else { /* * The call failed. Return empty strings for exe and args */ *args->str_storage = 0; argstr = ""; } /* * 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 (current->real_parent) ptid = current->parent->pid; 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 */ res = val_to_ring(args, (int64_t)rlimit(RLIMIT_NOFILE), 0, false, 0); 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; /* * clone-only parameters */ if (args->event_type == PPME_CLONE_16_X) { #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()); #else uint64_t euid = current_euid(); uint64_t egid = current_egid(); #endif /* * flags */ syscall_get_arguments(current, args->regs, 0, 1, &val); res = val_to_ring(args, (uint64_t)clone_flags_to_scap(val), 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * uid */ syscall_get_arguments(current, args->regs, 0, 1, &val); res = val_to_ring(args, euid, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * gid */ syscall_get_arguments(current, args->regs, 0, 1, &val); res = val_to_ring(args, egid, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; } return add_sentinel(args); } static int f_sys_socket_bind_x(struct event_filler_arguments *args) { int res; int64_t retval; int err = 0; u16 size = 0; struct sockaddr __user *usrsockaddr; unsigned long val; struct sockaddr_storage address; char *targetbuf = args->str_storage; /* * res */ retval = (int64_t)syscall_get_return_value(current, args->regs); res = val_to_ring(args, retval, 0, false, 0); /* * addr */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 1, 1, &val); #else val = args->socketcall_args[1]; #endif usrsockaddr = (struct sockaddr __user *)val; /* * Get the address len */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 2, 1, &val); #else val = args->socketcall_args[2]; #endif 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. */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 0, 1, &val); fd = (int)val; #else fd = (int)args->socketcall_args[0]; #endif if (fd >= 0) { /* * Get the address */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 1, 1, &val); #else val = args->socketcall_args[1]; #endif usrsockaddr = (struct sockaddr __user *)val; /* * Get the address len */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 2, 1, &val); #else val = args->socketcall_args[2]; #endif 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 */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 3, 1, &val); #else val = args->socketcall_args[3]; #endif if (unlikely(ppm_copy_from_user(fds, (const void __user *)val, sizeof(fds)))) return PPM_FAILURE_INVALID_USER_MEMORY; 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 val; 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 */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 0, 1, &srvskfd); #else srvskfd = args->socketcall_args[0]; #endif sock = sockfd_lookup(srvskfd, &err); if (unlikely(!sock || !(sock->sk))) { val = 0; if (sock) sockfd_put(sock); } else { if (sock->sk->sk_max_ack_backlog == 0) val = 0; else val = (unsigned long)sock->sk->sk_ack_backlog * 100 / sock->sk->sk_max_ack_backlog; sockfd_put(sock); } res = val_to_ring(args, val, 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 */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 0, 1, &val); #else val = args->socketcall_args[0]; #endif res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; *fd = val; /* * size */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 2, 1, &size); #else size = args->socketcall_args[2]; #endif 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); else 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 */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 4, 1, &val); #else val = args->socketcall_args[4]; #endif usrsockaddr = (struct sockaddr __user *)val; /* * Get the address len */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 5, 1, &val); #else val = args->socketcall_args[5]; #endif 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; /* * 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 { #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 1, 1, &val); #else val = args->socketcall_args[1]; #endif /* * The return value can be lower than the value provided by the user, * and we take that into account. */ bufsize = retval; } res = val_to_ring(args, val, min_t(unsigned long, bufsize, (unsigned long)g_snaplen), 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 */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 0, 1, &val); #else val = args->socketcall_args[0]; #endif res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * size */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 2, 1, &val); #else val = args->socketcall_args[2]; #endif 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); else 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); else 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; /* * 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 { #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 1, 1, &val); #else val = args->socketcall_args[1]; #endif /* * The return value can be lower than the value provided by the user, * and we take that into account. */ bufsize = *retval; } res = val_to_ring(args, val, min_t(unsigned long, bufsize, (unsigned long)g_snaplen), 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); else 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 */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 0, 1, &val); fd = (int)val; #else fd = (int)args->socketcall_args[0]; #endif /* * Get the address */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 4, 1, &val); #else val = args->socketcall_args[4]; #endif usrsockaddr = (struct sockaddr __user *)val; /* * Get the address len */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 5, 1, &val); #else val = args->socketcall_args[5]; #endif if (usrsockaddr != NULL && val != 0) { if (unlikely(ppm_copy_from_user(&addrlen, (const void __user *)val, sizeof(addrlen)))) return PPM_FAILURE_INVALID_USER_MEMORY; /* * 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; struct msghdr mh; char *targetbuf = args->str_storage; const struct iovec __user *iov; unsigned long iovcnt; int fd; u16 size = 0; int addrlen; int err = 0; struct sockaddr __user *usrsockaddr; struct sockaddr_storage address; /* * fd */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 0, 1, &val); #else val = args->socketcall_args[0]; #endif fd = val; res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * Retrieve the message header */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 1, 1, &val); #else val = args->socketcall_args[1]; #endif if (unlikely(ppm_copy_from_user(&mh, (const void __user *)val, sizeof(struct msghdr)))) 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, g_snaplen, PRB_FLAG_PUSH_SIZE); if (unlikely(res != PPM_SUCCESS)) return res; /* * tuple */ 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, 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; unsigned long iovcnt; struct msghdr mh; /* * 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 */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 1, 1, &val); #else val = args->socketcall_args[1]; #endif if (unlikely(ppm_copy_from_user(&mh, (const void __user *)val, sizeof(struct msghdr)))) return PPM_FAILURE_INVALID_USER_MEMORY; /* * data */ iov = (const struct iovec __user *)mh.msg_iov; iovcnt = mh.msg_iovlen; res = parse_readv_writev_bufs(args, iov, iovcnt, g_snaplen, PRB_FLAG_PUSH_DATA); if (unlikely(res != PPM_SUCCESS)) return res; return add_sentinel(args); } static int f_sys_recvmsg_e(struct event_filler_arguments *args) { int res; unsigned long val; /* * fd */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 0, 1, &val); #else val = args->socketcall_args[0]; #endif 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; unsigned long iovcnt; struct msghdr mh; 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 */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 1, 1, &val); #else val = args->socketcall_args[1]; #endif if (unlikely(ppm_copy_from_user(&mh, (const void __user *)val, sizeof(struct msghdr)))) 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); if (unlikely(res != PPM_SUCCESS)) return res; /* * tuple */ if (retval >= 0) { /* * Get the fd */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 0, 1, &val); fd = (int)val; #else fd = (int)args->socketcall_args[0]; #endif /* * 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); if (unlikely(ppm_copy_from_user(fds, (const void __user *)val, sizeof(fds)))) return PPM_FAILURE_INVALID_USER_MEMORY; 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)) { val = file->f_dentry->d_inode->i_ino; 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) { if (how == SHUT_RD) { return PPM_SHUT_RD; } else if (how == SHUT_WR) { return SHUT_WR; } else if (how == SHUT_RDWR) { return SHUT_RDWR; } else { ASSERT(false); return (u16)how; } } static int f_sys_shutdown_e(struct event_filler_arguments *args) { int res; unsigned long val; /* * fd */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 0, 1, &val); #else val = args->socketcall_args[0]; #endif res = val_to_ring(args, val, 0, false, 0); if (unlikely(res != PPM_SUCCESS)) return res; /* * how */ #ifndef __NR_socketcall syscall_get_arguments(current, args->regs, 1, 1, &val); #else val = args->socketcall_args[1]; #endif 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; else if (flt_op == FUTEX_WAIT_BITSET) res = PPM_FU_FUTEX_WAIT_BITSET; else if (flt_op == FUTEX_WAKE_BITSET) res = PPM_FU_FUTEX_WAKE_BITSET; else if (flt_op == FUTEX_WAIT_REQUEUE_PI) res = PPM_FU_FUTEX_WAIT_REQUEUE_PI; else if (flt_op == FUTEX_CMP_REQUEUE_PI) res = PPM_FU_FUTEX_CMP_REQUEUE_PI; if (op & FUTEX_PRIVATE_FLAG) res |= PPM_FU_FUTEX_PRIVATE_FLAG; if (op & FUTEX_CLOCK_REALTIME) res |= PPM_FU_FUTEX_CLOCK_REALTIME; 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; if (unlikely(ppm_copy_from_user(fds, (const void __user *)val, nfds * sizeof(struct pollfd)))) return PPM_FAILURE_INVALID_USER_MEMORY; 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 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); } 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; 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); iov = (const struct iovec __user *)val; syscall_get_arguments(current, args->regs, 2, 1, &iovcnt); 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; const struct iovec __user *iov; unsigned long iovcnt; unsigned int snaplen; /* * 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, 1, 1, &val); iov = (const struct iovec __user *)val; syscall_get_arguments(current, args->regs, 2, 1, &iovcnt); /* * Determine the snaplen by checking the fd type. * (note: not implemeted yet) */ snaplen = g_snaplen; #if 0 { int fd; int err, fput_needed; struct socket *sock; syscall_get_arguments(current, args->regs, 0, 1, &val); fd = (int)val; sock = ppm_sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { snaplen = g_snaplen; fput_light(sock->file, fput_needed); } else { snaplen = RW_SNAPLEN; } } #endif /* * Copy the buffer */ res = parse_readv_writev_bufs(args, iov, iovcnt, snaplen, PRB_FLAG_PUSH_SIZE); 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; const struct iovec __user *iov; unsigned long iovcnt; unsigned int snaplen; /* * 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); iov = (const struct iovec __user *)val; syscall_get_arguments(current, args->regs, 2, 1, &iovcnt); /* * Determine the snaplen by checking the fd type. * (note: not implemeted yet) */ snaplen = g_snaplen; #if 0 { int fd; int err, fput_needed; struct socket *sock; syscall_get_arguments(current, args->regs, 0, 1, &val); fd = (int)val; sock = ppm_sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { snaplen = g_snaplen; fput_light(sock->file, fput_needed); } else { snaplen = RW_SNAPLEN; } } #endif /* * Copy the buffer */ res = parse_readv_writev_bufs(args, iov, iovcnt, snaplen, PRB_FLAG_PUSH_DATA); 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; 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); iov = (const struct iovec __user *)val; syscall_get_arguments(current, args->regs, 2, 1, &iovcnt); 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 const struct iovec __user *iov; unsigned long iovcnt; unsigned int snaplen; /* * 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, 1, 1, &val); iov = (const struct iovec __user *)val; syscall_get_arguments(current, args->regs, 2, 1, &iovcnt); /* * Determine the snaplen by checking the fd type. * (note: not implemeted yet) */ snaplen = g_snaplen; #if 0 { int fd; int err, fput_needed; struct socket *sock; syscall_get_arguments(current, args->regs, 0, 1, &val); fd = (int)val; sock = ppm_sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { snaplen = g_snaplen; fput_light(sock->file, fput_needed); } else { snaplen = RW_SNAPLEN; } } #endif /* * Copy the buffer */ res = parse_readv_writev_bufs(args, iov, iovcnt, snaplen, PRB_FLAG_PUSH_SIZE); 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) { int res; uint64_t longtime; unsigned long val; char *targetbuf = args->str_storage; struct timespec *tts = (struct timespec *)targetbuf; int cfulen; /* * interval * We copy the timespec structure and then convert it to a 64bit relative time */ syscall_get_arguments(current, args->regs, 0, 1, &val); 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; res = val_to_ring(args, longtime, 0, false, 0); 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; case RLIMIT_RTTIME: return PPM_RLIMIT_RTTIME; 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; 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); 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; } 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; 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); 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; } } else { newcur = -1; newmax = -1; } syscall_get_arguments(current, args->regs, 3, 1, &val); 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; } /* * 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; /* // // 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; } */ 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, g_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 case F_SETOWN_EX: return PPM_FCNTL_F_SETOWN_EX; case F_GETOWN_EX: return PPM_FCNTL_F_GETOWN_EX; case F_SETLEASE: return PPM_FCNTL_F_SETLEASE; case F_GETLEASE: return PPM_FCNTL_F_GETLEASE; case F_CANCELLK: return PPM_FCNTL_F_CANCELLK; case F_DUPFD_CLOEXEC: return PPM_FCNTL_F_DUPFD_CLOEXEC; 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) { case PTRACE_SINGLEBLOCK: return PPM_PTRACE_SINGLEBLOCK; case PTRACE_SYSEMU_SINGLESTEP: return PPM_PTRACE_SYSEMU_SINGLESTEP; case PTRACE_SYSEMU: return PPM_PTRACE_SYSEMU; #ifdef PTRACE_ARCH_PRCTL case PTRACE_ARCH_PRCTL: return PPM_PTRACE_ARCH_PRCTL; #endif case PTRACE_SET_THREAD_AREA: return PPM_PTRACE_SET_THREAD_AREA; case PTRACE_GET_THREAD_AREA: return PPM_PTRACE_GET_THREAD_AREA; case PTRACE_OLDSETOPTIONS: return PPM_PTRACE_OLDSETOPTIONS; case PTRACE_SETFPXREGS: return PPM_PTRACE_SETFPXREGS; case PTRACE_GETFPXREGS: return PPM_PTRACE_GETFPXREGS; 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; len = ppm_copy_from_user(&dst, (const void __user *)val, sizeof(long)); 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; if (flags & MAP_32BIT) res |= PPM_MAP_32BIT; #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); } sysdig-0.1.87/driver/ppm_ringbuffer.h000066400000000000000000000025251237051215500175670ustar00rootroot00000000000000/* 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 = 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.1.87/driver/syscall_table.c000066400000000000000000001033731237051215500174010ustar00rootroot00000000000000/* 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 #include "ppm_ringbuffer.h" #include "ppm_events_public.h" #include "ppm_events.h" #include "ppm.h" /* * 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, 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_13_E, PPME_SYSCALL_EXECVE_13_X}, [__NR_clone - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_CLONE_16_E, PPME_CLONE_16_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_NEVER_DROP, PPME_SYSCALL_FUTEX_E, PPME_SYSCALL_FUTEX_X}, [__NR_stat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_STAT_E, PPME_SYSCALL_STAT_X}, [__NR_lstat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_LSTAT_E, PPME_SYSCALL_LSTAT_X}, [__NR_fstat - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_FSTAT_E, PPME_SYSCALL_FSTAT_X}, [__NR_epoll_wait - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_EPOLLWAIT_E, PPME_SYSCALL_EPOLLWAIT_X}, [__NR_poll - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_POLL_E, PPME_SYSCALL_POLL_X}, #ifdef __NR_select [__NR_select - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_SELECT_E, PPME_SYSCALL_SELECT_X}, #endif [__NR_lseek - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_LSEEK_E, PPME_SYSCALL_LSEEK_X}, [__NR_ioctl - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_IOCTL_E, PPME_SYSCALL_IOCTL_X}, [__NR_getcwd - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_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, PPME_SYSCALL_MKDIR_E, PPME_SYSCALL_MKDIR_X}, [__NR_rmdir - SYSCALL_TABLE_ID0] = {UF_USED, 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, PPME_SYSCALL_LINKAT_E, PPME_SYSCALL_LINKAT_X}, [__NR_unlink - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_UNLINK_E, PPME_SYSCALL_UNLINK_X}, [__NR_unlinkat - SYSCALL_TABLE_ID0] = {UF_USED, 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, PPME_SYSCALL_KILL_E, PPME_SYSCALL_KILL_X}, [__NR_tkill - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_TKILL_E, PPME_SYSCALL_TKILL_X}, [__NR_tgkill - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_TGKILL_E, PPME_SYSCALL_TGKILL_X}, [__NR_nanosleep - SYSCALL_TABLE_ID0] = {UF_USED, 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 | UF_NEVER_DROP, PPME_SYSCALL_FCNTL_E, PPME_SYSCALL_FCNTL_X}, #ifdef __NR_fcntl64 [__NR_fcntl64 - SYSCALL_TABLE_ID0] = {UF_USED | UF_NEVER_DROP, PPME_SYSCALL_FCNTL_E, PPME_SYSCALL_FCNTL_X}, #endif /* [__NR_ppoll - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, */ /* [__NR_old_select - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, */ [__NR_pselect6 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_epoll_create - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_epoll_ctl - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_uselib - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_sched_setparam - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_sched_getparam - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_fork - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_syslog - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_chmod - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_lchown - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_utime - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_mount - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_umount2 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_setuid - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_getuid - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_ptrace - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_PTRACE_E, PPME_SYSCALL_PTRACE_X}, [__NR_alarm - SYSCALL_TABLE_ID0] = {UF_USED, PPME_GENERIC_E, PPME_GENERIC_X}, [__NR_pause - SYSCALL_TABLE_ID0] = {UF_USED, 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, 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_E, PPME_SOCKET_ACCEPT_X}, [__NR_getsockname - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_GETSOCKNAME_E, PPME_SOCKET_GETSOCKNAME_X}, [__NR_getpeername - SYSCALL_TABLE_ID0] = {UF_USED, 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, PPME_SOCKET_SETSOCKOPT_E, PPME_SOCKET_SETSOCKOPT_X}, [__NR_getsockopt - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_GETSOCKOPT_E, PPME_SOCKET_GETSOCKOPT_X}, [__NR_sendmsg - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_SENDMSG_E, PPME_SOCKET_SENDMSG_X}, [__NR_accept4 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SOCKET_ACCEPT4_E, PPME_SOCKET_ACCEPT4_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, PPME_SYSCALL_STAT64_E, PPME_SYSCALL_STAT64_X}, #endif #ifdef __NR_fstat64 [__NR_fstat64 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_FSTAT64_E, PPME_SYSCALL_FSTAT64_X}, #endif #ifdef __NR__llseek [__NR__llseek - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_LLSEEK_E, PPME_SYSCALL_LLSEEK_X}, #endif [__NR_mmap - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_MMAP_E, PPME_SYSCALL_MMAP_X}, #ifdef __NR_mmap2 [__NR_mmap2 - SYSCALL_TABLE_ID0] = {UF_USED, PPME_SYSCALL_MMAP2_E, PPME_SYSCALL_MMAP2_X}, #endif [__NR_munmap - SYSCALL_TABLE_ID0] = {UF_USED, 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 }; /* * 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 }; sysdig-0.1.87/scripts/000077500000000000000000000000001237051215500146015ustar00rootroot00000000000000sysdig-0.1.87/scripts/CMakeLists.txt000066400000000000000000000005241237051215500173420ustar00rootroot00000000000000configure_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) sysdig-0.1.87/scripts/completions/000077500000000000000000000000001237051215500171355ustar00rootroot00000000000000sysdig-0.1.87/scripts/completions/bash/000077500000000000000000000000001237051215500200525ustar00rootroot00000000000000sysdig-0.1.87/scripts/completions/bash/sysdig000066400000000000000000000054201237051215500213000ustar00rootroot00000000000000# This completion code is based on the cmdline interface as it exists in sysdig # 0.1.87 complete -W \ ' \ -A \ --print-ascii \ -cl \ --list-chisels \ -d \ --displayflt \ -D \ --debug \ -F \ --fatfile \ -h \ --help \ -j \ --json \ -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 \ -s \ --snaplen \ -t \ --timetype \ -c \ --chisel \ -i \ --chisel-info' sysdig # Sysdig 0.1.85 had these options too, but they were problematic, so I'm # removing them until they can be fixed # -C # --file-size # -G # --seconds # -W # --limit # Local Variables: # mode:sh # End: sysdig-0.1.87/scripts/completions/zsh/000077500000000000000000000000001237051215500177415ustar00rootroot00000000000000sysdig-0.1.87/scripts/completions/zsh/_sysdig000066400000000000000000000242041237051215500213270ustar00rootroot00000000000000#compdef sysdig # This completion code is based on the cmdline interface as it exists in sysdig # 0.1.87 # this must match fields_info.cpp local DESCRIPTION_TEXT_START=20 # 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") _describe -t fields 'Fields' fields _describe -t operators 'Operators' ops elif [[ $1 == "format" ]]; then # expanding format specifiers local allfields_withpercent; 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 alts_types[$#alts_types]+=$alts_chisels[*]'))' 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\"" 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]' \ '(-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]' \ '(-F --fatfile)'{-F,--fatfile}'[Enable fatfile mode]' \ '(-h --help)'{-h,--help}'[Print this help]' \ '(-j --json)'{-j,--json}'[Emit output as JSON]' \ '(-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]' \ '(-P --progress)'{-P,--progress}'[Print progress on stderr while processing trace files]' \ '(-q --quiet)'{-q,--quiet}'[Do not print events on the screen]' \ '(-S --summary)'{-S,--summary}'[Print the event summary when the capture ends]' \ '(-v --verbose)'{-v,--verbose}'[Verbose output]' \ '(-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]' \ '(-n --numevents)'{-n,--numevents=-}'[Stop capturing after events]:Max events:' \ '(-p --print)'{-p,--print=-}'[Specify the event format]:Event output format:->format' \ '(-r --read)'{-r,--read=-}'[Read events from ]:Input file:_files -g "*.scap"' \ '(-w --write)'{-w,--write=-}'[Write events to ]:Output file:_files -g "*.scap"' \ '(-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"))' \ '(-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 # Sysdig 0.1.85 had these options too, but they were problematic, so I'm # removing them until they can be fixed # '(-C --file-size)'{-C,--file-size=-}'[Chunk captures to files of given size]:Maximum chunk file size:' # '(-G --seconds)'{-G,--seconds=-}'[Rotate the capture file every seconds]:Rotation period (seconds):' # '(-W --limit)'{-W,--limit}'[Limit split captures (-C or -G) to a given number of files]:Maximum number of files' 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.1.87/scripts/debian/000077500000000000000000000000001237051215500160235ustar00rootroot00000000000000sysdig-0.1.87/scripts/debian/postinst.in000066400000000000000000000012671237051215500202440ustar00rootroot00000000000000#!/bin/sh set -e DKMS_PACKAGE_NAME="sysdig" DKMS_VERSION="@SYSDIG_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.1.87/scripts/debian/prerm.in000066400000000000000000000004361237051215500175030ustar00rootroot00000000000000#!/bin/sh set -e DKMS_PACKAGE_NAME="sysdig" DKMS_VERSION="@SYSDIG_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.1.87/scripts/description.txt000066400000000000000000000006231237051215500176660ustar00rootroot00000000000000Sysdig 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.1.87/scripts/install-sysdig.in000066400000000000000000000107771237051215500201130ustar00rootroot00000000000000#!/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 ]; then rpm --quiet -i http://mirrors.kernel.org/fedora-epel/beta/7/x86_64/epel-release-7-0.1.noarch.rpm else rpm --quiet -i http://mirrors.kernel.org/fedora-epel/6/i386/epel-release-6-8.noarch.rpm fi fi echo "* Installing Draios public key" rpm --quiet --import https://s3.amazonaws.com/download.draios.com/DRAIOS-GPG-KEY.public echo "* Installing Draios 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 fi echo "* Installing Draios public key" curl -s https://s3.amazonaws.com/download.draios.com/DRAIOS-GPG-KEY.public | apt-key add - echo "* Installing Draios repository" curl -s -o /etc/apt/sources.list.d/draios.list http://download.draios.com/_REPOSITORY_NAME_/deb/draios.list apt-get -qq update echo "* Installing kernel headers" apt-get -qq -y install linux-headers-$(uname -r) || kernel_warning echo "* Installing Sysdig" apt-get -qq -y install sysdig } 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 if [ $DISTRO = "Ubuntu" ]; then VERSION=$(echo $VERSION | cut -d'.' -f1) if [ $VERSION -ge 10 ]; then install_deb else unsupported fi elif [ $DISTRO = "LinuxMint" ]; then if [ $VERSION -ge 9 ]; then install_deb else unsupported fi elif [ $DISTRO = "Debian" ]; then if [ $VERSION -ge 6 ]; then install_deb elif [[ $VERSION == *sid* ]]; then install_deb else unsupported fi else unsupported fi 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') if [ $DISTRO = "oracle" ] || [ $DISTRO = "centos" ] || [ $DISTRO = "redhat" ]; then if [ $VERSION -ge 6 ]; then install_rpm else unsupported fi elif [ $DISTRO = "amazon" ]; then install_rpm elif [ $DISTRO = "fedoraproject" ]; then if [ $VERSION -ge 13 ]; then install_rpm else unsupported fi else unsupported fi else unsupported fi sysdig-0.1.87/scripts/rpm/000077500000000000000000000000001237051215500153775ustar00rootroot00000000000000sysdig-0.1.87/scripts/rpm/postinstall000066400000000000000000000011111237051215500176700ustar00rootroot00000000000000dkms 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 -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.1.87/scripts/rpm/preuninstall000066400000000000000000000000751237051215500200440ustar00rootroot00000000000000dkms remove -m sysdig -v %{version} --all --rpm_safe_upgrade sysdig-0.1.87/test/000077500000000000000000000000001237051215500140715ustar00rootroot00000000000000sysdig-0.1.87/test/sysdig_batch_parser.sh000077500000000000000000000021441237051215500204500ustar00rootroot00000000000000#!/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 -r $f $ARGS > $DIRNAME/$(basename $f).output done echo Data saved in $DIRNAME echo Comparing diff -r $DIRNAME $REFERENCEDIR echo No change rm -rf $DIRNAME sysdig-0.1.87/test/sysdig_trace_regression.sh000077500000000000000000000112441237051215500213520ustar00rootroot00000000000000#!/bin/bash set -exu SCRIPT=$(readlink -f $0) BASEDIR=$(dirname $SCRIPT) SYSDIG=$1 CHISELS=$2 TMPBASE=${3:-$(mktemp -d --tmpdir sysdig.XXXXXXXXXX)} TRACEDIR="${TMPBASE}/traces" RESULTDIR="${TMPBASE}/results" BASELINEDIR="${TMPBASE}/baseline" 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 https://s3.amazonaws.com/download.draios.com/sysdig-tests/baseline.zip unzip baseline.zip rm -rf baseline.zip cd - fi echo "Executing sysdig tests in ${TMPBASE}" # Fields $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-p\"*%fd.num %fd.type %fd.typechar %fd.name %fd.directory %fd.filename %fd.cip %fd.sip %fd.cport %fd.sport %fd.l4proto %fd.sockfamily %fd.is_server\"" $TRACEDIR $RESULTDIR/fd_fields $BASELINEDIR/fd_fields $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-p%thread.exectime" $TRACEDIR $RESULTDIR/exetime $BASELINEDIR/exetime # Category: CPU Usage $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopprocs_cpu" $TRACEDIR $RESULTDIR/topprocs_cpu $BASELINEDIR/topprocs_cpu # Category: I/O $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cecho_fds" $TRACEDIR $RESULTDIR/echo_fds $BASELINEDIR/echo_fds $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cfdbytes_by fd.name" $TRACEDIR $RESULTDIR/fdbytes_by $BASELINEDIR/fdbytes_by $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cfdcount_by fd.name" $TRACEDIR $RESULTDIR/fdcount_by $BASELINEDIR/fdcount_by $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ciobytes" $TRACEDIR $RESULTDIR/iobytes $BASELINEDIR/iobytes $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ciobytes_file" $TRACEDIR $RESULTDIR/iobytes_file $BASELINEDIR/iobytes_file $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cstderr" $TRACEDIR $RESULTDIR/stderr $BASELINEDIR/stderr $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cstdin" $TRACEDIR $RESULTDIR/stdin $BASELINEDIR/stdin $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cstdout" $TRACEDIR $RESULTDIR/stdout $BASELINEDIR/stdout $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopfiles_bytes" $TRACEDIR $RESULTDIR/topfiles_bytes $BASELINEDIR/topfiles_bytes $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopfiles_time" $TRACEDIR $RESULTDIR/topfiles_time $BASELINEDIR/topfiles_time $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopprocs_file" $TRACEDIR $RESULTDIR/topprocs_file $BASELINEDIR/topprocs_file # Category: Net $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ciobytes_net" $TRACEDIR $RESULTDIR/iobytes_net $BASELINEDIR/iobytes_net $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cspy_ip 127.0.0.1" $TRACEDIR $RESULTDIR/spy_ip $BASELINEDIR/spy_ip $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cspy_port 80" $TRACEDIR $RESULTDIR/spy_port $BASELINEDIR/spy_port $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopconns" $TRACEDIR $RESULTDIR/topconns $BASELINEDIR/topconns $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopports_server" $TRACEDIR $RESULTDIR/topports_server $BASELINEDIR/topports_server $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopprocs_net" $TRACEDIR $RESULTDIR/topprocs_net $BASELINEDIR/topprocs_net # Category: Performance $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cbottlenecks" $TRACEDIR $RESULTDIR/bottlenecks $BASELINEDIR/bottlenecks $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cfileslower 1000" $TRACEDIR $RESULTDIR/fileslower $BASELINEDIR/fileslower $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cnetlower 10" $TRACEDIR $RESULTDIR/netlower $BASELINEDIR/netlower $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cproc_exec_time" $TRACEDIR $RESULTDIR/proc_exec_time $BASELINEDIR/proc_exec_time $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cscallslower 1000" $TRACEDIR $RESULTDIR/scallslower $BASELINEDIR/scallslower $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopscalls" $TRACEDIR $RESULTDIR/topscalls $BASELINEDIR/topscalls $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopscalls_time" $TRACEDIR $RESULTDIR/topscalls_time $BASELINEDIR/topscalls_time # Category: Security $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-cspy_users" $TRACEDIR $RESULTDIR/spy_users $BASELINEDIR/spy_users # Category: Errors $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopfiles_errors" $TRACEDIR $RESULTDIR/topfiles_errors $BASELINEDIR/topfiles_errors $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-ctopprocs_errors" $TRACEDIR $RESULTDIR/topprocs_errors $BASELINEDIR/topprocs_errors # JSON $BASEDIR/sysdig_batch_parser.sh $SYSDIG $CHISELS "-j -n 10000" $TRACEDIR $RESULTDIR/fd_fields_json $BASELINEDIR/fd_fields_json rm -rf "${TMPBASE}" sysdig-0.1.87/userspace/000077500000000000000000000000001237051215500151045ustar00rootroot00000000000000sysdig-0.1.87/userspace/.gitignore000066400000000000000000000000051237051215500170670ustar00rootroot00000000000000test sysdig-0.1.87/userspace/common/000077500000000000000000000000001237051215500163745ustar00rootroot00000000000000sysdig-0.1.87/userspace/common/sysdig_types.h000066400000000000000000000022521237051215500212740ustar00rootroot00000000000000/* 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.1.87/userspace/libscap/000077500000000000000000000000001237051215500165215ustar00rootroot00000000000000sysdig-0.1.87/userspace/libscap/CMakeLists.txt000066400000000000000000000013721237051215500212640ustar00rootroot00000000000000include_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) add_dependencies(scap zlib) 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.1.87/userspace/libscap/doxygen/000077500000000000000000000000001237051215500201765ustar00rootroot00000000000000sysdig-0.1.87/userspace/libscap/doxygen/conf.dox000066400000000000000000000176061237051215500216510ustar00rootroot00000000000000# 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.1.87/userspace/libscap/doxygen/footer.html000066400000000000000000000000301237051215500223530ustar00rootroot00000000000000 sysdig-0.1.87/userspace/libscap/doxygen/header.html000066400000000000000000000026111237051215500223140ustar00rootroot00000000000000--- layout: default title: sysdig | libscap ---

$projectname $projectnumber

(Generated by Doxygen)

$projectbrief

$projectbrief
$searchbox
sysdig-0.1.87/userspace/libscap/dynamic_params_table.c000066400000000000000000000014621237051215500230260ustar00rootroot00000000000000/* 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.1.87/userspace/libscap/event_table.c000066400000000000000000000605161237051215500211650ustar00rootroot00000000000000/* 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_NONE, 1, {{"size", PT_UINT32, PF_DEC} } }, /* PPME_SYSCALL_BRK_1_X */{"brk", EC_MEMORY, EF_NONE, 1, {{"res", PT_UINT64, PF_HEX} } }, /* PPME_SYSCALL_EXECVE_8_E */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_EXECVE_8_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 8, {{"res", PT_ERRNO, PF_DEC}, {"exe", PT_CHARBUF, PF_NA}, {"args", PT_BYTEBUF, PF_NA}, {"tid", PT_PID, PF_DEC}, {"pid", PT_PID, PF_DEC}, {"ptid", PT_PID, PF_DEC}, {"cwd", PT_CHARBUF, PF_NA}, {"fdlimit", PT_UINT64, PF_DEC} } }, /* PPME_CLONE_11_E */{"clone", EC_PROCESS, EF_MODIFIES_STATE, 0}, /* PPME_CLONE_11_X */{"clone", EC_PROCESS, EF_MODIFIES_STATE, 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, 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), 0}, /* PPME_SOCKET_ACCEPT_X */{"accept", EC_NET, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 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), 1, {{"flags", PT_INT32, PF_HEX} } }, /* PPME_SOCKET_ACCEPT4_X */{"accept", EC_NET, (enum ppm_event_flags)(EF_CREATES_FD | EF_MODIFIES_STATE), 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_E */{"ioctl", EC_IO_OTHER, EF_USES_FD, 2, {{"fd", PT_FD, PF_DEC}, {"request", PT_UINT64, PF_HEX} } }, /* PPME_SYSCALL_IOCTL_X */{"ioctl", EC_IO_OTHER, EF_USES_FD, 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, EF_NONE, 1, {{"next", PT_PID, PF_DEC} } }, /* PPME_SCHEDSWITCH_1_X */{"NA2", EC_SCHEDULER, EF_UNUSED, 0}, /* PPME_DROP_E */{"drop", EC_INTERNAL, EF_NONE, 1, {{"ratio", PT_UINT32, PF_DEC} } }, /* PPME_DROP_X */{"drop", EC_INTERNAL, EF_NONE, 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, EF_MODIFIES_STATE, 0}, /* PPME_SYSCALL_EXECVE_13_X */{"execve", EC_PROCESS, EF_MODIFIES_STATE, 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, 0}, /* PPME_CLONE_16_X */{"clone", EC_PROCESS, EF_MODIFIES_STATE, 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} } }, }; sysdig-0.1.87/userspace/libscap/examples/000077500000000000000000000000001237051215500203375ustar00rootroot00000000000000sysdig-0.1.87/userspace/libscap/examples/01-open/000077500000000000000000000000001237051215500215165ustar00rootroot00000000000000sysdig-0.1.87/userspace/libscap/examples/01-open/CMakeLists.txt000066400000000000000000000002201237051215500242500ustar00rootroot00000000000000include_directories("../../../common") include_directories("../../") add_executable(scap-open test.c) target_link_libraries(scap-open scap) sysdig-0.1.87/userspace/libscap/examples/01-open/test.c000066400000000000000000000020301237051215500226340ustar00rootroot00000000000000/* 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 int main(int argc, char** argv) { char error[SCAP_LASTERR_SIZE]; int32_t res; scap_evt* ev; uint16_t cpuid; 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; } } scap_close(h); return 0; } sysdig-0.1.87/userspace/libscap/examples/02-validatebuffer/000077500000000000000000000000001237051215500235415ustar00rootroot00000000000000sysdig-0.1.87/userspace/libscap/examples/02-validatebuffer/CMakeLists.txt000066400000000000000000000002431237051215500263000ustar00rootroot00000000000000include_directories("../../../common") include_directories("../..") add_executable(scap-validatebuffer test.c) target_link_libraries(scap-validatebuffer scap) sysdig-0.1.87/userspace/libscap/examples/02-validatebuffer/test.c000066400000000000000000000136571237051215500247000ustar00rootroot00000000000000/* 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]; } return res + j * sizeof(uint16_t) + sizeof(struct ppm_evt_hdr) + sizeof(uint32_t); } 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); if(buf_len < sizeof(struct ppm_evt_hdr) + g_event_info[hdr->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) { printf("bps:%" PRIu64 " totbytes:%" PRIu64 " - evts/s:%" PRIu64 " totevs:%" PRIu64 "\n", totbytes - oldtotbytes, totbytes, totevents - oldtotevents, totevents); oldtotbytes = totbytes; oldtotevents = totevents; nloops = 0; } nloops++; } scap_close(h); return 0; } sysdig-0.1.87/userspace/libscap/flags_table.c000066400000000000000000000220561237051215500211350ustar00rootroot00000000000000/* 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_NONE", PPM_O_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}, {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}, }; 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}, }; sysdig-0.1.87/userspace/libscap/scap-int.h000066400000000000000000000156361237051215500204230ustar00rootroot00000000000000/* 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 // // The time scap_next will wait when a buffer is empty // #define BUFFER_EMPTY_WAIT_TIME_MS 30 // // 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 // uint64_t m_sn_next_ts; // timestamp }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; uint32_t m_emptybuf_timeout_ms; scap_addrlist* m_addrlist; scap_machine_info m_machine_info; scap_userlist* m_userlist; }; // // 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); // 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, scap_fdinfo * sockets, char *error); // read tcp or udp sockets from the proc filesystem int32_t scap_fd_read_ipv4_sockets_from_proc_fs(scap_t* handle, 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, scap_fdinfo** 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); uint32_t scap_event_compute_len(scap_evt* e); // // 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 #ifdef __cplusplus } #endif sysdig-0.1.87/userspace/libscap/scap.c000066400000000000000000000556641237051215500176330ustar00rootroot00000000000000/* Copyright (C) 2013-2014 Draios inc. This file is part of sysdig. sysdig is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. sysdig is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with sysdig. If not, see . */ #include #include #ifndef _WIN32 #include #include #include #include #include #include #include #endif // _WIN32 #include "scap.h" #include "../../driver/ppm_ringbuffer.h" #include "scap_savefile.h" #include "scap-int.h" //#define NDEBUG #include char* scap_getlasterr(scap_t* handle) { return handle->m_lasterr; } scap_t* scap_open_live(char *error) { #if !defined(HAS_CAPTURE) snprintf(error, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME); return NULL; #else uint32_t j; char dev[255]; scap_t* handle = NULL; int len; uint32_t ndevs; uint32_t res; // // 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_ndevs = 0; handle->m_proclist = NULL; handle->m_file = NULL; handle->m_file_evt_buf = NULL; handle->m_evtcnt = 0; handle->m_addrlist = NULL; handle->m_userlist = NULL; handle->m_emptybuf_timeout_ms = BUFFER_EMPTY_WAIT_TIME_MS; handle->m_last_evt_dump_flags = 0; // // Find out how many devices we have to open, which equals to the number of CPUs // ndevs = sysconf(_SC_NPROCESSORS_ONLN); // // 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_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; // // 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(scap_create_userlist(handle) != SCAP_SUCCESS) { scap_close(handle); snprintf(error, SCAP_LASTERR_SIZE, "error creating the interface list"); return NULL; } // // Create the process list // error[0] = '\0'; if((res = scap_proc_scan_proc_dir(handle, "/proc", -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; } 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; // // Open and initialize all the devices // for(j = 0; j < handle->m_ndevs; j++) { // // Open the device // sprintf(dev, "/dev/sysdig%d", j); if((handle->m_devs[j].m_fd = open(dev, O_RDWR | O_SYNC)) < 0) { if(errno == EBUSY) { snprintf(error, SCAP_LASTERR_SIZE, "device %s is already open. You can't run multiple instances of sysdig.", dev); } else { snprintf(error, SCAP_LASTERR_SIZE, "error opening device %s. Make sure you have root credentials and that the sysdig-probe module is loaded.", dev); } 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", dev); 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", dev); return NULL; } // // Additional initializations // handle->m_devs[j].m_lastreadsize = 0; handle->m_devs[j].m_sn_len = 0; scap_stop_dropping_mode(handle); } return handle; #endif // HAS_CAPTURE } scap_t* scap_open_offline(const char* fname, char *error) { 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_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_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; } // // 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; //scap_proc_print_table(handle); return handle; } int32_t scap_set_empty_buffer_timeout_ms(scap_t* handle, uint32_t timeout_ms) { handle->m_emptybuf_timeout_ms = timeout_ms; return SCAP_SUCCESS; } 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_patform 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; } // // Does the user want to block? // if(blocking) { // // If we are asked to operate in blocking mode, keep waiting until at least // MIN_USERSPACE_READ_SIZE bytes are in the buffer. // while(true) { get_buf_pointers(handle->m_devs[cpuid].m_bufinfo, &thead, &ttail, &read_size); if(read_size >= MIN_USERSPACE_READ_SIZE) { break; } usleep(BUFFER_EMPTY_WAIT_TIME_MS * 1000); } } else { // // If we are not asked to block, read the pointers and keep going. // get_buf_pointers(handle->m_devs[cpuid].m_bufinfo, &thead, &ttail, &read_size); } // // logic check // XXX should probably be an assertion, but for the moment we want to print some meaningful info and // stop the processing. // if((handle->m_devs[cpuid].m_bufinfo->tail + read_size) % RING_BUF_SIZE != thead) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "buffer corruption. H=%u, T=%u, R=%u, S=%u (%u)", thead, handle->m_devs[cpuid].m_bufinfo->tail, read_size, RING_BUF_SIZE, (handle->m_devs[cpuid].m_bufinfo->tail + read_size) % RING_BUF_SIZE); ASSERT(false); return SCAP_FAILURE; } #if 0 printf("%u)H:%u T:%u Used:%u Free:%u Size=%u\n", cpuid, thead, ttail, read_size, (uint32_t)(RING_BUF_SIZE - read_size - 1), (uint32_t)RING_BUF_SIZE); #endif // // 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; for(j = 0; j < handle->m_ndevs; j++) { uint32_t thead; uint32_t ttail; uint32_t read_size; get_buf_pointers(handle->m_devs[j].m_bufinfo, &thead, &ttail, &read_size); if(read_size > 100000) { return false; } } return true; } #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; bool waited = false; *pcpuid = 65535; for(j = 0; j < handle->m_ndevs; j++) { if(handle->m_devs[j].m_sn_len == 0) { // // The buffer for this CPU is fully consumed. // This is a good time to check if we should wait // if(handle->m_emptybuf_timeout_ms != 0) { if(check_scap_next_wait(handle) && !waited) { usleep(BUFFER_EMPTY_WAIT_TIME_MS * 1000); waited = true; } } // // read another buffer // int32_t res = scap_readbuf(handle, j, false, &handle->m_devs[j].m_sn_next_event, &handle->m_devs[j].m_sn_len); if(res != SCAP_SUCCESS) { return res; } } // // Make sure that we have data // if(handle->m_devs[j].m_sn_len != 0) { if(handle->m_devs[j].m_sn_len > max_buf_size) { max_buf_size = handle->m_devs[j].m_sn_len; } // // We want to consume the event with the lowest timestamp // pe = (scap_evt*)handle->m_devs[j].m_sn_next_event; #ifdef _DEBUG ASSERT(pe->len == scap_event_compute_len(pe)); #endif if(pe->ts < max_ts) { if(pe->len > handle->m_devs[j].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 { // // This happens only when all the buffers are empty, which should be very rare. // The caller want't receive an event, but shouldn't treat this as an error and should just retry. // return SCAP_TIMEOUT; } #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; } } // // Since no new data is going to be produced, we disable read waits so that the remaining data // can be consumd without slowdowns // handle->m_emptybuf_timeout_ms = 0; 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; } sysdig-0.1.87/userspace/libscap/scap.def000066400000000000000000000014351237051215500201320ustar00rootroot00000000000000LIBRARY scap EXPORTS scap_open_live scap_open_offline 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_flagssysdig-0.1.87/userspace/libscap/scap.h000066400000000000000000000571761237051215500176400ustar00rootroot00000000000000/* 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 * @{ */ // // 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 /*! \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]; ///< Full command name (e.g. "/bin/top") char args[SCAP_MAX_PATH_SIZE]; ///< Command line arguments (e.g. "-d1") uint16_t args_len; ///< Command line arguments 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 scap_fdinfo* fdlist; ///< The fd table for this process UT_hash_handle hh; ///< makes this structure hashable }scap_threadinfo; // // 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_patform { 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_patform; /*! \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 // // Forward declarations // typedef struct scap scap_t; typedef struct ppm_evt_hdr scap_evt; /////////////////////////////////////////////////////////////////////////////// // 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 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_patform 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 This function can be used to specify the time after which \ref scap_next returns when no events are available. \param handle Handle to the capture instance. \param handle The number of milliseconds after which scap_next will return SCAP_TIMEOUT. Use 0 if you want to the scap_next to always return immediately. \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_set_empty_buffer_timeout_ms(scap_t* handle, uint32_t timeout_ms); /*! \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); /*@}*/ /////////////////////////////////////////////////////////////////////////////// // 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); 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); #ifdef __cplusplus } #endif sysdig-0.1.87/userspace/libscap/scap.vcxproj000066400000000000000000000146701237051215500210740ustar00rootroot00000000000000 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.1.87/userspace/libscap/scap.vcxproj.filters000066400000000000000000000051511237051215500225350ustar00rootroot00000000000000 {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.1.87/userspace/libscap/scap_event.c000066400000000000000000000037561237051215500210270ustar00rootroot00000000000000/* 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[]; // // Get the event info table // const struct ppm_event_info* scap_get_event_info_table() { 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_compute_len(scap_evt* e) { uint32_t j; uint32_t res = 0; uint16_t* lens = (uint16_t*)((char*)e + sizeof(struct ppm_evt_hdr)); ASSERT(e->type < PPM_EVENT_MAX); for(j = 0; j < g_event_info[e->type].nparams; j++) { res += lens[j]; } res += g_event_info[e->type].nparams * sizeof(uint16_t) + sizeof(struct ppm_evt_hdr); #ifdef PPM_ENABLE_SENTINEL res += sizeof(uint32_t); #endif return res; } 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.1.87/userspace/libscap/scap_fds.c000066400000000000000000001030631237051215500204520ustar00rootroot00000000000000/* 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 #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: printf("type = %d\n", fdi->type); 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 lenght 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_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); // ASSERT(false); // snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "duplicate fd %"PRIu64"for process%"PRIu64, fdi->fd, tinfo->tid); // return SCAP_FAILURE; } // // Add the fd to the table // 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; } 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; int32_t res; 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); } res = scap_add_fd_to_proc_table(handle, tinfo, fdi); return res; } int32_t scap_fd_handle_socket(scap_t *handle, char *fname, scap_threadinfo *tinfo, scap_fdinfo *fdi, scap_fdinfo *sockets, char *error) { char link_name[1024]; ssize_t r; scap_fdinfo *tfdi; uint64_t ino; if(sockets == NULL) { return SCAP_SUCCESS; } 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, &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, 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("/proc/net/unix", "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) { // TODO: set some error message break; } } fclose(f); return uth_status; } int32_t scap_fd_read_ipv4_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 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, scap_fdinfo **sockets) { if(SCAP_FAILURE == scap_fd_read_ipv4_sockets_from_proc_fs(handle, "/proc/net/tcp", SCAP_L4_TCP, sockets) || SCAP_FAILURE == scap_fd_read_ipv4_sockets_from_proc_fs(handle, "/proc/net/udp", SCAP_L4_UDP, sockets) || SCAP_FAILURE == scap_fd_read_ipv4_sockets_from_proc_fs(handle, "/proc/net/raw", SCAP_L4_RAW, sockets) || SCAP_FAILURE == scap_fd_read_unix_sockets_from_proc_fs(handle, sockets)) { scap_fd_free_table(handle, sockets); return SCAP_FAILURE; } /* We assume if there is /proc/net/tcp6 that ipv6 is avaiable */ if(0 == access("/proc/net/tcp6", R_OK)) { if(SCAP_FAILURE == scap_fd_read_ipv6_sockets_from_proc_fs(handle, "/proc/net/tcp6", SCAP_L4_TCP, sockets) || SCAP_FAILURE == scap_fd_read_ipv6_sockets_from_proc_fs(handle, "/proc/net/udp6", SCAP_L4_UDP, sockets) || SCAP_FAILURE == scap_fd_read_ipv6_sockets_from_proc_fs(handle, "/proc/net/raw6", SCAP_L4_RAW, sockets)) { scap_fd_free_table(handle, 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, scap_fdinfo *sockets, char *error) { DIR *dir_p; struct dirent *dir_entry_p; int32_t res = SCAP_SUCCESS; char fd_dir_name[1024]; char f_name[1024]; struct stat sb; uint64_t fd; scap_fdinfo *fdi = NULL; 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; } 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, sockets, error); if(fdi->type == SCAP_FD_UNKNOWN) { // we can land here if we've got a netlink socket 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(NULL != fdi) { ASSERT(SCAP_FD_UNKNOWN != fdi->type); } 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.1.87/userspace/libscap/scap_iflist.c000066400000000000000000000137011237051215500211670ustar00rootroot00000000000000/* 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; } #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.1.87/userspace/libscap/scap_procs.c000066400000000000000000000323451237051215500210300ustar00rootroot00000000000000/* 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 #endif #include "scap.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; 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 // int32_t scap_proc_fill_flimit(uint64_t tid, struct scap_threadinfo* tinfo) #ifdef SYS_prlimit64 { struct rlimit rl; if(syscall(SYS_prlimit64, tid, RLIMIT_NOFILE, NULL, &rl) == 0) { tinfo->fdlimit = rl.rlim_cur; return SCAP_SUCCESS; } tinfo->fdlimit = -1; return SCAP_SUCCESS; } #else { tinfo->fdlimit = -1; return SCAP_SUCCESS; } #endif // // Add a process to the list by parsing its entry under /proc // int32_t scap_proc_add_from_proc(scap_t* handle, uint32_t tid, int parenttid, int tid_to_scan, char* procdirname, scap_fdinfo* sockets, scap_threadinfo** procinfo, char *error) { char dir_name[256]; char target_name[256]; int target_res; char filename[252]; char line[SCAP_MAX_PATH_SIZE]; struct scap_threadinfo* tinfo; int32_t uth_status = SCAP_SUCCESS; FILE* f; size_t filesize; size_t exe_len; 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) { // // This is normal and happens with kernel threads, which we aren't interested in // return SCAP_SUCCESS; } target_name[target_res] = 0; // // 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; } 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; } snprintf(tinfo->exe, SCAP_MAX_PATH_SIZE, "%s", target_name); // // Gather the command name // snprintf(filename, sizeof(filename), "%sstatus", dir_name); f = fopen(filename, "r"); if(f == NULL) { snprintf(error, SCAP_LASTERR_SIZE, "can't open %s", filename); free(tinfo); return SCAP_FAILURE; } else { 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 { filesize = fread(line, 1, sizeof(line), f); line[filesize - 1] = 0; exe_len = strlen(line); if(exe_len < filesize) { ++exe_len; } tinfo->args_len = filesize - exe_len; if(tinfo->args_len > SCAP_MAX_PATH_SIZE) { tinfo->args_len = SCAP_MAX_PATH_SIZE; } memcpy(tinfo->args, line + exe_len, tinfo->args_len); tinfo->args[SCAP_MAX_PATH_SIZE - 1] = 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 tid_to_scan is set we assume is a runtime lookup so no // need to use the table // if(tid_to_scan == -1) { // // Done. Add the entry to the process table // 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 { *procinfo = tinfo; } // // Only add fds for processes, not threads // if(-1 == parenttid) { return scap_fd_scan_fd_dir(handle, dir_name, tinfo, sockets, error); } return SCAP_SUCCESS; } // // 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]; scap_fdinfo* sockets = 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) { if(SCAP_FAILURE == scap_fd_read_sockets(handle, &sockets)) { closedir(dir_p); return SCAP_FAILURE; } } } 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, 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); scap_fd_free_table(handle, &sockets); 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; if(scap_proc_scan_proc_dir(handle, "/proc", -1, tid, &tinfo, handle->m_lasterr, scan_sockets) != SCAP_SUCCESS) { return NULL; } return tinfo; #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.1.87/userspace/libscap/scap_savefile.c000077500000000000000000001311711237051215500215000ustar00rootroot00000000000000/* 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) { 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(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) { 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; // // First pass pass of the table to calculate the length // HASH_ITER(hh, handle->m_proclist, tinfo, ttinfo) { 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 sizeof(uint32_t)); } // // Create the block // bh.block_type = PL_BLOCK_TYPE_V2; 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) { 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); 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)) { 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) { scap_proc_free_table(handle); if(scap_proc_scan_proc_dir(handle, "/proc", -1, -1, NULL, handle->m_lasterr, true) != SCAP_SUCCESS) { return NULL; } } #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; } // // 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; 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_PATH_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; if(block_type == PL_BLOCK_TYPE_V2 || block_type == PL_BLOCK_TYPE_V2_INT) { // // 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; } // // 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; // // All parsed. Add the entry to the table // 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; } } // // 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); 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; // // 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; } 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; // // 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; // // Add the entry to the table // 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; } } // // 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_V1_INT: case PL_BLOCK_TYPE_V2_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."); ASSERT(false); return SCAP_FAILURE; } if(!found_ul) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Can't find user list block."); ASSERT(false); return SCAP_FAILURE; } if(!found_il) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Can't find interface list block."); ASSERT(false); return SCAP_FAILURE; } if(!found_fdl) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Can't find file descriptor list block."); ASSERT(false); return SCAP_FAILURE; } if(!found_pl) { snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "corrupted input file. Can't find process list block."); ASSERT(false); 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)) { 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; } sysdig-0.1.87/userspace/libscap/scap_savefile.h000066400000000000000000000112361237051215500215010ustar00rootroot00000000000000/* 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 /////////////////////////////////////////////////////////////////////////////// // 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.1.87/userspace/libscap/scap_userlist.c000066400000000000000000000075141237051215500215540ustar00rootroot00000000000000/* 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; strncpy(handle->m_userlist->users[usercnt].name, p->pw_name, sizeof(handle->m_userlist->users[usercnt].name)); strncpy(handle->m_userlist->users[usercnt].homedir, p->pw_dir, sizeof(handle->m_userlist->users[usercnt].homedir)); strncpy(handle->m_userlist->users[usercnt].shell, p->pw_shell, sizeof(handle->m_userlist->users[usercnt].shell)); 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; strncpy(handle->m_userlist->groups[grpcnt].name, g->gr_name, sizeof(handle->m_userlist->groups[grpcnt].name)); 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.1.87/userspace/libscap/settings.h000066400000000000000000000002731237051215500205340ustar00rootroot00000000000000// // 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.1.87/userspace/libscap/stdint_win.h000066400000000000000000000170601237051215500210600ustar00rootroot00000000000000// 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.1.87/userspace/libscap/syscall_info_table.c000066400000000000000000000474361237051215500225370ustar00rootroot00000000000000/* 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" } }; sysdig-0.1.87/userspace/libscap/uthash.h000066400000000000000000001643651237051215500202050ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/000077500000000000000000000000001237051215500167275ustar00rootroot00000000000000sysdig-0.1.87/userspace/libsinsp/.gitignore000066400000000000000000000000121237051215500207100ustar00rootroot00000000000000unit_testssysdig-0.1.87/userspace/libsinsp/CMakeLists.txt000066400000000000000000000013601237051215500214670ustar00rootroot00000000000000include_directories(./) include_directories(../../common) include_directories(../libscap) include_directories("${JSONCPP_INCLUDE}") include_directories("${LUAJIT_INCLUDE}") add_library(sinsp STATIC chisel.cpp chisel_api.cpp cyclewriter.cpp event.cpp eventformatter.cpp dumper.cpp fdinfo.cpp filter.cpp filterchecks.cpp ifinfo.cpp memmem.cpp internal_metrics.cpp "${JSONCPP_LIB_SRC}" logger.cpp parsers.cpp protodecoder.cpp threadinfo.cpp sinsp.cpp stats.cpp utils.cpp) target_link_libraries(sinsp scap "${JSONCPP_LIB}") if(NOT WIN32) add_dependencies(sinsp luajit) target_link_libraries(sinsp "${LUAJIT_LIB}" dl) else() target_link_libraries(sinsp "${LUAJIT_LIB}") endif() sysdig-0.1.87/userspace/libsinsp/chisel.cpp000066400000000000000000000512011237051215500207010ustar00rootroot00000000000000/* 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" #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_filter", &lua_cbacks::get_filter}, {"get_machine_info", &lua_cbacks::get_machine_info}, {"get_thread_table", &lua_cbacks::get_thread_table}, {"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}, {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; #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 #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(); 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)) { goto failure; } 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"); 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)); } // // 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"); } // // Push the arguments // for(j = 0; j < m_argvals.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, m_lua_script_info.m_args[j].m_name.c_str()); lua_pushstring(m_ls, m_argvals[j].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) { lua_pushlightuserdata(m_ls, evt); lua_setglobal(m_ls, "sievt"); 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); // // 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(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) { first_event_inits(evt); } 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::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.1.87/userspace/libsinsp/chisel.h000066400000000000000000000062041237051215500203510ustar00rootroot00000000000000/* 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; 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; }; 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; 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); uint32_t get_n_args(); uint32_t get_n_optional_args(); uint32_t get_n_required_args(); void set_args(string args); bool run(sinsp_evt* evt); void do_timeout(sinsp_evt* evt); void on_init(); void on_capture_start(); void on_capture_end(); bool get_nextrun_args(OUT string* args); private: bool openfile(string filename, OUT ifstream* is); void free_lua_chisel(); 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.1.87/userspace/libsinsp/chisel_api.cpp000066400000000000000000000447671237051215500215550ustar00rootroot00000000000000/* 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" #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: 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_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); 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::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_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; 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); 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) { // // 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, "progid"); lua_pushnumber(ls, (uint32_t)it->second.m_progid); 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()) { ASSERT(false); 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 and populate the FD table // lua_pushstring(ls, "fdtable"); sinsp_fdtable* fdtable = it->second.get_fd_table(); lua_newtable(ls); for(fdit = fdtable->m_table.begin(); fdit != fdtable->m_table.end(); ++fdit) { 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); 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); } 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; } #endif // HAS_LUA_CHISELS #endif // HAS_CHISELS sysdig-0.1.87/userspace/libsinsp/chisel_api.h000066400000000000000000000034041237051215500212010ustar00rootroot00000000000000/* 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 is_live(lua_State *ls); static int is_tty(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_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); }; #endif // HAS_CHISELS sysdig-0.1.87/userspace/libsinsp/cyclewriter.cpp000066400000000000000000000174661237051215500220050ustar00rootroot00000000000000#include "sinsp.h" #include "sinsp_int.h" #include "cyclewriter.h" cycle_writer::cycle_writer() : m_base_file_name(""), m_rollover_mb(0), m_duration_seconds(0), m_file_limit(0), m_do_cycle(false), m_byte_count(0), m_last_time(0), m_file_count_total(0), m_file_index(0), m_first_consider(false) { // // 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; } bool cycle_writer::setup(string base_file_name, int rollover_mb, int duration_seconds, int file_limit, bool do_cycle) { if(m_first_consider) { return false; } m_base_file_name = base_file_name; m_rollover_mb = rollover_mb; m_duration_seconds = duration_seconds; m_file_limit = file_limit; m_do_cycle = do_cycle; // // Seed the filename with an initial // value. // consider(0); 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()) // * DOQUI - end the capture. // cycle_writer::conclusion cycle_writer::consider(long byte_count) { m_byte_count += byte_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 = time(0); } // // If the current time is more than the last, // this is beyond our permissable limit // if(time(0) - m_last_time >= m_duration_seconds) { // Reset the last time to now. m_last_time = time(0); // Reset our file numbering back to 0. // // NOTE: tcpdump doesn't do this necessarily and // it's pretty confusing. m_file_index = 0; // Also bring back our byte_count since this // will be a new file to consider. m_byte_count = byte_count; // Set the last reason m_last_reason = "Maximum Time Reached"; // Create a new file name and recommend it. return next_file(); } } if(m_rollover_mb > 0) { // // If we've rolled over the amount of bytes we // are supposed to write to a specific file then // if(m_byte_count > m_rollover_mb * 1000 * 1000) { // Reset the counter back to the amount // we considered to write this time m_byte_count = byte_count; // Set the last reason m_last_reason = "Maximum File Size Reached"; // // Increment our naming convention and // return our advice. // 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(); } // // If we got here, this means that none // of our limits were hit, so we // recommend using the same file. // return SAMEFILE; } // // get_current_file_name - returns the name of the // currently recommended file given the input parameters // 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 we were told to only write a limited number // of files // if (m_file_limit > 0) { // // If we are not cycling and the total number // of files recommended exceeds the maximum // number that we've been told to consider // if(!m_do_cycle && m_file_count_total >= m_file_limit) { // // We've reached our limit and have // been instructed not to cycle in a ring, // so we recommend a DOQUIT signaling the // end of the capture. // m_last_reason = "Maximum Number of Capture Files Written"; return DOQUIT; } // // Otherwise, we see if our current index exceeds // our limit // if(m_file_index >= m_file_limit) { // // If so, we reset the index back to zero. The // file_index will by definition always be equal // to or less then the file_count_total so we don't // need to check for the cycle here because it will // be caught above and return out of the function. // m_file_index = 0; } } // // If we've made it here then we need to // do a new file name. // // The consider() block will move the // m_last_time forward if needed so we // can trust that the value we have at // this point is valid. // // Our file name is base + number // when applicable. // if(m_duration_seconds > 0) { // // This means that we need to run strftime // over the base name. We'll just allocate // a good static 400 bytes here for the file // name ... 260 is usually considered max path, // the edge cases where it isn't is really // outside the scope of this comment block // ... really. // const size_t our_size = 400; 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: we failed ... } // We now have our "base" filename m_last_file_name = file_name; } else { // // This means we aren't using the duration // flag so we don't have to run the stftime // function ... our job is really easy! // m_last_file_name = m_base_file_name; } // // If we've specified to limit the number of // bytes per file and we have made it here, // that means that we should append the // current value of m_file_index (which was dealt // with above) on to the end of m_last_file_name // which we created from above. // if(m_file_limit > 0) { // // If the first character value is null (explicitly set // above in the constructor), this means we haven't created // our format for the file numbering yet, so we do it now. // if(m_limit_format[0] == 0) { // The maximum numbr of decimal digits we need. int digit_count = 0; // A temporary copy of the file limit for // our exploitation and fun. int our_file_limit = m_file_limit; // // In order to determine how many digits we // need to express up to our file_limit // // we just continuually divide by 10 until // we get zero. // // It's really not bad. // while(our_file_limit > 0) { digit_count++; our_file_limit /= 10; } // // Now we can construct our format which will // actually be put inside another snprintf() later on // snprintf( // The format we are trying to derive m_limit_format, sizeof(m_limit_format), // // Read the string below like this: // // %% 0(%d) d // ^^^^ // '- This is the only part that // this snprintf cares about. // // A value of "5" will yield the following: // // __ // %%0%dd // |/ // __ v // %%05d // |/ // v // %05d // // Which iw what we want. // "%%0%dd", digit_count ); } // // Now that we have our format string, we can take // our base file name and append the current number // to it. We need to allocate a new char for that // first. This is a number, we'll set aside 22 bytes // for it just to prepare for the 45th century AD // when that kind of storage comes free with every // cereal box. // char index[22]; snprintf(index, sizeof(index), m_limit_format, m_file_index); // Tack the index string value on to our base. m_last_file_name += index; } // Increment the total number of files. m_file_count_total++; // Increment the current index. m_file_index++; // Return that we've recommended a new file. return NEWFILE; } sysdig-0.1.87/userspace/libsinsp/cyclewriter.h000066400000000000000000000057241237051215500214440ustar00rootroot00000000000000#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. DOQUIT }; cycle_writer(); ~cycle_writer() {}; // // 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, bool do_cycle); // // Consider byte_count bytes 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(long byte_count); // // 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 int m_rollover_mb; // = 0 // Time in seconds between captures int m_duration_seconds; // = 0 // Total number of allowed captures int m_file_limit; // = 0 // Whether to cycle the names at all bool m_do_cycle; // = false private: // Total number of bytes written int m_byte_count; // 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; }; sysdig-0.1.87/userspace/libsinsp/doxygen/000077500000000000000000000000001237051215500204045ustar00rootroot00000000000000sysdig-0.1.87/userspace/libsinsp/doxygen/conf.dox000066400000000000000000000264561237051215500220620ustar00rootroot00000000000000# 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.1.87/userspace/libsinsp/doxygen/footer.html000066400000000000000000000000301237051215500225610ustar00rootroot00000000000000
sysdig-0.1.87/userspace/libsinsp/doxygen/header.html000066400000000000000000000024221237051215500225220ustar00rootroot00000000000000--- layout: default title: sysdig | libsinsp ---

$projectname $projectnumber

(Generated by Doxygen)

$projectbrief

$projectbrief
$searchbox
sysdig-0.1.87/userspace/libsinsp/dumper.cpp000066400000000000000000000040521237051215500207300ustar00rootroot00000000000000/* 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)); } } 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.1.87/userspace/libsinsp/dumper.h000066400000000000000000000033501237051215500203750ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/event.cpp000066400000000000000000001206721237051215500205640ustar00rootroot00000000000000/* 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 #endif #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_param implementation /////////////////////////////////////////////////////////////////////////////// void sinsp_evt_param::init(char *valptr, uint16_t len) { m_val = valptr; m_len = len; } /////////////////////////////////////////////////////////////////////////////// // 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 } 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 } sinsp_evt::~sinsp_evt() { } void sinsp_evt::init() { m_params_loaded = false; m_info = scap_event_getinfo(m_pevt); m_tinfo = NULL; m_fdinfo = NULL; m_iosize = 0; } void sinsp_evt::init(uint8_t *evdata, uint16_t cpuid) { m_params_loaded = false; m_pevt = (scap_evt *)evdata; m_info = scap_event_getinfo(m_pevt); m_tinfo = NULL; m_fdinfo = NULL; m_iosize = 0; m_cpuid = cpuid; m_evtnum = 0; } uint64_t sinsp_evt::get_num() { return m_evtnum; } int16_t sinsp_evt::get_cpuid() { return m_cpuid; } uint16_t sinsp_evt::get_type() { return m_pevt->type; } ppm_event_flags sinsp_evt::get_flags() { return m_info->flags; } 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 = (uint16_t*)ptr; if(ptr == src + srclen - 1) { k += sprintf(row + k, " %.2x", *((uint8_t*)chunk)); } else { k += sprintf(row + k, " %.4x", *chunk); } num_chunks++; ptr += sizeof(uint16_t); } if(fmt & sinsp_evt::PF_HEXASCII) { // 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_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_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::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()); 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::null; 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((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_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; 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; } 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) { snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%u.%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], (unsigned int)*(uint16_t*)(payload+5)); } 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) { snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%u.%u.%u.%u:%u->%u.%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], (unsigned int)*(uint16_t*)(payload+5), (unsigned int)(uint8_t)payload[7], (unsigned int)(uint8_t)payload[8], (unsigned int)(uint8_t)payload[9], (unsigned int)(uint8_t)payload[10], (unsigned int)*(uint16_t*)(payload+11)); } 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)) { snprintf(&m_paramstr_storage[0], m_paramstr_storage.size(), "%u.%u.%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)*(uint16_t*)(payload + 17), (unsigned int)dip[0], (unsigned int)dip[1], (unsigned int)dip[2], (unsigned int)dip[3], (unsigned int)*(uint16_t*)(payload + 35)); 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:%u->%s:%u", srcstr, (unsigned int)*(uint16_t*)(payload + 17), dststr, (unsigned int)*(uint16_t*)(payload + 35)); 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; 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((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; 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::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]; } } void sinsp_evt::get_category(OUT sinsp_evt::category* cat) { if(get_type() == PPME_GENERIC_E || get_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; cat->m_category = g_infotables.m_syscall_info_table[id].category; cat->m_subcategory = sinsp_evt::SC_NONE; } 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; return dflags; } } else { *should_drop = true; } } return dflags; } #endif sysdig-0.1.87/userspace/libsinsp/event.h000066400000000000000000000207321237051215500202250ustar00rootroot00000000000000/* 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'. }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: void init(char* valptr, uint16_t 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 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 }; /*! \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. */ uint64_t get_num(); /*! \brief Get the number of the CPU where this event was captured. */ int16_t get_cpuid(); /*! \brief Get the event type. \note For a list of event types, refer to \ref etypes. */ uint16_t get_type(); /*! \brief Get the event's flags. */ ppm_event_flags get_flags(); /*! \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 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); void init(); void init(uint8_t* evdata, uint16_t cpuid); void load_params(); 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 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; }; /*@}*/ sysdig-0.1.87/userspace/libsinsp/eventformatter.cpp000066400000000000000000000132201237051215500224760ustar00rootroot00000000000000/* 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); 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_inspector->get_buffer_format() == sinsp_evt::PF_JSON && !m_first) { (*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) { Json::Value json_value = m_tokens[j]->tojson(evt); if(retval == false) { continue; } if(json_value == Json::Value::null && 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) { 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.1.87/userspace/libsinsp/eventformatter.h000066400000000000000000000043561237051215500221550ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/fdinfo.cpp000066400000000000000000000232361237051215500207060ustar00rootroot00000000000000/* 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; } 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) { /* bool is_sip_local = inspector->get_ifaddr_list()->is_ipv4addr_in_local_machine(pfdinfo->m_sockinfo.m_ipv4info.m_fields.m_sip); bool is_dip_local = inspector->get_ifaddr_list()->is_ipv4addr_in_local_machine(pfdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip); // // If only the client is local, mark the role as client. // If only the server is local, mark the role as server. // if(is_sip_local) { if(!is_dip_local) { pfdinfo->set_role_client(); return true; } } else if(is_dip_local) { if(!is_sip_local) { pfdinfo->set_role_server(); return true; } } // // Both addresses are local // ASSERT(is_sip_local && is_dip_local); */ // // 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) { switch(etype) { case CT_READ: m_read_callbacks.push_back(dec); break; case CT_WRITE: 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; switch(etype) { case CT_READ: for(it = m_read_callbacks.begin(); it != m_read_callbacks.end(); ++it) { if(*it == dec) { m_read_callbacks.erase(it); return; } } break; case CT_WRITE: for(it = m_write_callbacks.begin(); it != m_write_callbacks.end(); ++it) { if(*it == dec) { 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::find(int64_t fd) { unordered_map::iterator fdit = m_table.find(fd); // // 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); } } sinsp_fdinfo_t* sinsp_fdtable::add(int64_t fd, sinsp_fdinfo_t* fdinfo) { pair::iterator, bool> insert_res; insert_res = m_table.insert(std::make_pair(fd,*fdinfo)); // // Look for the FD in the table // if(insert_res.second == true) { // // 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 } else { // // the fd is already in the table. // if(insert_res.first->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] = insert_res.first->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 // insert_res.first->second = *fdinfo; } return &(insert_res.first->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.1.87/userspace/libsinsp/fdinfo.h000066400000000000000000000163101237051215500203460ustar00rootroot00000000000000/* 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; /*! \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(); string* tostring(); /*! \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; } /*! \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); 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. 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); bool is_role_server() { return (m_flags & FLAGS_ROLE_SERVER) == FLAGS_ROLE_SERVER; } bool is_role_client() { return (m_flags & FLAGS_ROLE_CLIENT) == FLAGS_ROLE_CLIENT; } bool is_role_none() { return (m_flags & (FLAGS_ROLE_CLIENT | FLAGS_ROLE_SERVER)) == 0; } bool is_transaction() { return (m_flags & FLAGS_TRANSACTION) == FLAGS_TRANSACTION; } void set_is_transaction() { m_flags |= FLAGS_TRANSACTION; } void set_role_server() { m_flags |= FLAGS_ROLE_SERVER; } 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); void reset_flags() { m_flags = FLAGS_NONE; } void set_socketpipe() { m_flags |= FLAGS_IS_SOCKET_PIPE; } bool is_socketpipe() { return (m_flags & FLAGS_IS_SOCKET_PIPE) == FLAGS_IS_SOCKET_PIPE; } bool has_no_role() { return !is_role_client() && !is_role_server(); } T m_usrstate; uint32_t m_flags; uint64_t m_ino; vector m_write_callbacks; vector m_read_callbacks; 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; }; /*@}*/ /////////////////////////////////////////////////////////////////////////////// // fd info table /////////////////////////////////////////////////////////////////////////////// class sinsp_fdtable { public: sinsp_fdtable(sinsp* inspector); sinsp_fdinfo_t* find(int64_t fd); // 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.1.87/userspace/libsinsp/filter.cpp000066400000000000000000000722731237051215500207330ustar00rootroot00000000000000/* 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 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()); } 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()); 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; } /////////////////////////////////////////////////////////////////////////////// // 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); default: throw sinsp_exception("'contains' 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_LT: throw sinsp_exception("'<' not supported for string filters"); case CO_LE: throw sinsp_exception("'<=' not supported for string filters"); case CO_GT: throw sinsp_exception("'>' not supported for string filters"); case CO_GE: throw sinsp_exception("'>=' not supported for string filters"); 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(ppm_cmp_operator op, ppm_param_type type, void* operand1, void* operand2, uint32_t op1_len, uint32_t op2_len) { 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)*(int8_t*)operand1, (uint64_t)*(int8_t*)operand2); case PT_FLAGS16: case PT_UINT16: case PT_PORT: case PT_SYSCALLID: return flt_compare_uint64(op, (uint64_t)*(int16_t*)operand1, (uint64_t)*(int16_t*)operand2); case PT_UINT32: case PT_FLAGS32: case PT_BOOL: case PT_IPV4ADDR: return flt_compare_uint64(op, (uint64_t)*(int32_t*)operand1, (uint64_t)*(int32_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_SOCKADDR: case PT_SOCKTUPLE: case PT_FDLIST: case PT_FSPATH: 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_nfiedls = -1; m_val_storage_len = 0; } 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) { 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::null; } case PT_INT16: if(finfo->m_print_format == PF_DEC) { 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::null; } case PT_INT32: if(finfo->m_print_format == PF_DEC) { 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::null; } case PT_INT64: case PT_PID: if(finfo->m_print_format == PF_DEC) { 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) { 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::null; } case PT_PORT: // This can be resolved in the future case PT_UINT16: if(finfo->m_print_format == PF_DEC) { 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::null; } case PT_UINT32: if(finfo->m_print_format == PF_DEC) { 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::null; } case PT_UINT64: case PT_RELTIME: case PT_ABSTIME: if(finfo->m_print_format == PF_DEC) { 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::null; } case PT_SOCKADDR: case PT_SOCKFAMILY: ASSERT(false); return Json::Value::null; 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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; 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: // This can be resolved in the future 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::null) { uint8_t* rawval = extract(evt, &len); if(rawval == NULL) { return Json::Value::null; } return rawval_to_json(rawval, m_field, len); } return jsonval; } int32_t sinsp_filter_check::parse_field_name(const char* str) { int32_t j; int32_t max_fldlen = -1; ASSERT(m_info.m_fields != NULL); ASSERT(m_info.m_nfiedls != -1); string val(str); m_field_id = 0xffffffff; for(j = 0; j < m_info.m_nfiedls; 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; bool chkres; for(j = 0; j < size; j++) { sinsp_filter_check* chk = m_checks[j]; ASSERT(chk != NULL); chkres = chk->compare(evt); if(j == 0) { switch(chk->m_boolop) { case BO_NONE: res = chkres; break; case BO_NOT: res = !chkres; break; default: ASSERT(false); break; } } else { switch(chk->m_boolop) { case BO_OR: res = res || chkres; break; case BO_AND: res = res && chkres; break; case BO_ORNOT: res = res || !chkres; break; case BO_ANDNOT: res = res && !chkres; break; default: ASSERT(false); break; } } } return res; } /////////////////////////////////////////////////////////////////////////////// // sinsp_filter implementation /////////////////////////////////////////////////////////////////////////////// sinsp_filter::sinsp_filter(sinsp* inspector, const string& fltstr) { m_inspector = inspector; 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) { 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))) || (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 == ')') { 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_ERROR; 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(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 { 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); 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)); } ppm_cmp_operator co = next_comparison_operator(); vector operand2 = next_operand(false); chk->m_boolop = op; chk->m_cmpop = co; chk->parse_field_name((char *)&operand1[0]); 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: parse_check(m_curexpr, m_last_boolop); m_state = ST_EXPRESSION_DONE; break; } } vector components = sinsp_split(m_fltstr, ' '); } bool sinsp_filter::run(sinsp_evt *evt) { return m_filter->compare(evt); } #endif // HAS_FILTERING sysdig-0.1.87/userspace/libsinsp/filter.h000066400000000000000000000046261237051215500203750ustar00rootroot00000000000000/* 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 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. \note Throws a sinsp_exception if the filter syntax is not valid. */ sinsp_filter(sinsp* inspector, const string& fltstr); ~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); 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; 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.1.87/userspace/libsinsp/filterchecks.cpp000066400000000000000000001631211237051215500221050ustar00rootroot00000000000000/* 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; /////////////////////////////////////////////////////////////////////////////// // sinsp_filter_check_fd implementation /////////////////////////////////////////////////////////////////////////////// const filtercheck_field_info sinsp_filter_check_fd_fields[] = { {PT_INT64, EPF_NONE, PF_DEC, "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_PORT, EPF_FILTER_ONLY, PF_DEC, "fd.port", "matches the port (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_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_DEC, "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."}, }; 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_nfiedls = sizeof(sinsp_filter_check_fd_fields) / sizeof(sinsp_filter_check_fd_fields[0]); } sinsp_filter_check* sinsp_filter_check_fd::allocate_new() { return (sinsp_filter_check*) new sinsp_filter_check_fd(); } int32_t sinsp_filter_check_fd::parse_field_name(const char* str) { return sinsp_filter_check::parse_field_name(str); } 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_ACCEPT4_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_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 + 1); } } else { 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_ACCEPT4_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: 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); } } 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: { 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 + 1); } } else { 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; } 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_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_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_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_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; } m_tbool = m_inspector->get_ifaddr_list()->is_ipv4addr_in_local_machine(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dip); 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_IPV4_SOCK || m_fdinfo->m_type == SCAP_FD_IPV6_SOCK) { m_tstr = "unix"; return (uint8_t*)m_tstr.c_str(); } else { return NULL; } } 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) { scap_fd_type evt_type = m_fdinfo->m_type; if(evt_type == SCAP_FD_IPV4_SOCK) { if(m_cmpop == CO_EQ) { if(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport == *(uint16_t*)&m_val_storage[0] || m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport == *(uint16_t*)&m_val_storage[0]) { return true; } } else if(m_cmpop == CO_NE) { if(m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_sport != *(uint16_t*)&m_val_storage[0] && m_fdinfo->m_sockinfo.m_ipv4info.m_fields.m_dport != *(uint16_t*)&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_port == *(uint16_t*)&m_val_storage[0]) { return true; } } else if(evt_type == SCAP_FD_IPV6_SOCK) { if(m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_sport == *(uint16_t*)&m_val_storage[0] || m_fdinfo->m_sockinfo.m_ipv6info.m_fields.m_dport == *(uint16_t*)&m_val_storage[0]) { return true; } } else if(evt_type == SCAP_FD_IPV6_SERVSOCK) { if(m_fdinfo->m_sockinfo.m_ipv6serverinfo.m_port == *(uint16_t*)&m_val_storage[0]) { return true; } } } 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) { 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_DEC, "proc.pid", "the id of the process generating the event."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.exe", "the full name (including the path) of the executable generating the event."}, {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.cmdline", "full process command line, i.e name + arguments."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.cwd", "the current working directory of the event."}, {PT_UINT32, EPF_NONE, PF_DEC, "proc.nchilds", "the number of child threads of that the process generating the event currently has."}, {PT_INT64, EPF_NONE, PF_DEC, "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_DEC, "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_DEC, "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_UINT64, 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_DEC, "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_UINT64, EPF_NONE, PF_DEC, "iobytes", "I/O bytes (either read or write) generated by I/O calls like read, write, send receive..."}, // {PT_UINT64, EPF_NONE, PF_DEC, "totiobytes", "aggregated number of I/O bytes (either read or write) since the beginning of the capture."}, // {PT_RELTIME, EPF_NONE, PF_DEC, "latency", "number of nanoseconds spent in the last system call."}, // {PT_RELTIME, EPF_NONE, PF_DEC, "totlatency", "aggregated number of nanoseconds spent in system calls since the beginning of the capture.."}, }; 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_nfiedls = sizeof(sinsp_filter_check_thread_fields) / sizeof(sinsp_filter_check_thread_fields[0]); m_u64val = 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(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); } return parsed_len; } int32_t sinsp_filter_check_thread::parse_field_name(const char* str) { 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 // m_th_state_id = m_inspector->reserve_thread_memory(sizeof(uint64_t)); return sinsp_filter_check::parse_field_name(str); } else { return sinsp_filter_check::parse_field_name(str); } } 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(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_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_CWD: m_tstr = tinfo->get_cwd(); return (uint8_t*)m_tstr.c_str(); 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_IOBYTES: case TYPE_TOTIOBYTES: { // // Extract the return value // uint16_t etype = evt->get_type(); uint64_t res; if(etype == PPME_SYSCALL_READ_X || etype == PPME_SYSCALL_WRITE_X || etype == PPME_SOCKET_RECV_X || etype == PPME_SOCKET_SEND_X|| etype == PPME_SOCKET_RECVFROM_X || etype == PPME_SOCKET_RECVMSG_X || etype == PPME_SOCKET_SENDTO_X || etype == PPME_SOCKET_SENDMSG_X || etype == PPME_SYSCALL_READV_X || etype == PPME_SYSCALL_WRITEV_X || etype == PPME_SYSCALL_PREAD_X || etype == PPME_SYSCALL_PWRITE_X || etype == PPME_SYSCALL_PREADV_X || etype == PPME_SYSCALL_PWRITEV_X) { sinsp_evt_param *parinfo = evt->get_param(0); ASSERT(parinfo->m_len == sizeof(int64_t)); res = *(int64_t *)parinfo->m_val; } else { res = 0; } if(m_field_id == TYPE_IOBYTES) { m_u64val = res; if(m_u64val != 0) { return (uint8_t*)&m_u64val; } else { return NULL; } } else { m_u64val += res; return (uint8_t*)&m_u64val; } } case TYPE_LATENCY: if(tinfo->m_latency != 0) { return (uint8_t*)&tinfo->m_latency; } else { return NULL; } case TYPE_TOTLATENCY: m_u64val += tinfo->m_latency; return (uint8_t*)&m_u64val; 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_u64val = tinfo->get_fd_usage_pct(); return (uint8_t*)&m_u64val; 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_PFMAJOR: m_u64val = tinfo->m_pfmajor; return (uint8_t*)&m_u64val; case TYPE_PFMINOR: m_u64val = tinfo->m_pfminor; return (uint8_t*)&m_u64val; 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 NULL; } sinsp_threadinfo* mt = NULL; if(tinfo->is_main_thread()) { mt = tinfo; } else { mt = tinfo->get_main_thread(); if(mt == NULL) { return NULL; } } // // 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 NULL; } sinsp_threadinfo* mt = NULL; if(tinfo->is_main_thread()) { mt = tinfo; } else { mt = tinfo->get_main_thread(); if(mt == NULL) { return NULL; } } // // 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_DEC, "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."}, {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_CHARBUF, EPF_PRINT_ONLY, PF_NA, "evt.dir", "event direction can be either '>' for enter events or '<' for exit events."}, {PT_CHARBUF, EPF_NONE, PF_NA, "evt.type", "For system call events, this is the name of the system call (e.g. 'open')."}, {PT_INT16, EPF_NONE, PF_DEC, "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. 'resarg.fd' or 'resarg[0]'."}, {PT_DYN, EPF_REQUIRES_ARGUMENT, PF_NA, "evt.rawarg", "one of the event arguments specified by name. E.g. 'arg.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_CHARBUF, EPF_NONE, PF_DEC, "evt.res", "event return value, as an error code string (e.g. 'ENOENT')."}, {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_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_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."}, }; sinsp_filter_check_event::sinsp_filter_check_event() { m_first_ts = 0; m_is_compare = false; m_info.m_name = "evt"; m_info.m_fields = sinsp_filter_check_event_fields; m_info.m_nfiedls = sizeof(sinsp_filter_check_event_fields) / sizeof(sinsp_filter_check_event_fields[0]); } 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::parse_field_name(const char* str) { 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") { // // These fields need to store the previuos event type in the thread state // m_th_state_id = m_inspector->reserve_thread_memory(sizeof(uint16_t)); return sinsp_filter_check::parse_field_name(str); } else { return sinsp_filter_check::parse_field_name(str); } } 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; 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); } } 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; } } 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: 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: return (Json::Value::Int64)*(uint64_t*)extract(evt, len); case TYPE_COUNT: m_u32val = 1; return m_u32val; default: return Json::Value::null; } return Json::Value::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: if(m_first_ts == 0) { m_first_ts = evt->get_ts(); } m_u64val = evt->get_ts() - m_first_ts; return (uint8_t*)&m_u64val; case TYPE_RELTS_S: if(m_first_ts == 0) { m_first_ts = evt->get_ts(); } m_u64val = (evt->get_ts() - m_first_ts) / ONE_SECOND_IN_NS; return (uint8_t*)&m_u64val; case TYPE_RELTS_NS: if(m_first_ts == 0) { m_first_ts = evt->get_ts(); } m_u64val = (evt->get_ts() - m_first_ts) % ONE_SECOND_IN_NS; return (uint8_t*)&m_u64val; case TYPE_LATENCY: { m_u64val = 0; if(evt->get_direction() == SCAP_ED_IN) { if(evt->m_tinfo != NULL) { uint16_t* pt = (uint16_t*)evt->m_tinfo->get_private_state(m_th_state_id); *pt = evt->get_type(); } return (uint8_t*)&m_u64val; } if(evt->m_tinfo != NULL) { uint16_t* pt = (uint16_t*)evt->m_tinfo->get_private_state(m_th_state_id); if(evt->m_tinfo->m_prevevent_ts && evt->get_type() == *pt + 1) { m_u64val = (evt->get_ts() - evt->m_tinfo->m_prevevent_ts); } } return (uint8_t*)&m_u64val; } case TYPE_LATENCY_S: case TYPE_LATENCY_NS: { m_u64val = 0; if(evt->get_direction() == SCAP_ED_IN) { if(evt->m_tinfo != NULL) { uint16_t* pt = (uint16_t*)evt->m_tinfo->get_private_state(m_th_state_id); *pt = evt->get_type(); } return (uint8_t*)&m_u64val; } if(evt->m_tinfo != NULL) { uint16_t* pt = (uint16_t*)evt->m_tinfo->get_private_state(m_th_state_id); if(evt->m_tinfo->m_prevevent_ts && evt->get_type() == *pt + 1) { if(m_field_id == TYPE_LATENCY_S) { m_u64val = (evt->get_ts() - evt->m_tinfo->m_prevevent_ts) / 1000000000; } else { m_u64val = (evt->get_ts() - evt->m_tinfo->m_prevevent_ts) % 1000000000; } } } return (uint8_t*)&m_u64val; } case TYPE_DIR: if(PPME_IS_ENTER(evt->get_type())) { return (uint8_t*)">"; } else { return (uint8_t*)"<"; } case TYPE_TYPE: { uint8_t* evname; if(evt->m_pevt->type == PPME_GENERIC_E || evt->m_pevt->type == 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_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) { char* il; vector* cbacks = &(fdinfo->m_write_callbacks); vector::iterator it; for(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_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; argstr = evt->get_param_value_str("res", &resolved_argstr); if(resolved_argstr != NULL && resolved_argstr[0] != 0) { return (uint8_t*)resolved_argstr; } else { if(argstr == NULL) { if((evt->get_flags() & EF_CREATES_FD) && PPME_IS_EXIT(evt->get_type())) { argstr = evt->get_param_value_str("fd", &resolved_argstr); if(resolved_argstr != NULL && resolved_argstr[0] != 0) { return (uint8_t*)resolved_argstr; } else { return (uint8_t*)argstr; } } else { return NULL; } } else { return (uint8_t*)argstr; } } } 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_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; 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_DEC, "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_nfiedls = sizeof(sinsp_filter_check_user_fields) / sizeof(sinsp_filter_check_user_fields[0]); } 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(); ASSERT(userlist->size() != 0); if(tinfo->m_uid == 0xffffffff) { return NULL; } it = userlist->find(tinfo->m_uid); if(it == userlist->end()) { ASSERT(false); 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_DEC, "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_nfiedls = sizeof(sinsp_filter_check_group_fields) / sizeof(sinsp_filter_check_group_fields[0]); } 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) { 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_nfiedls = 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) { int32_t res = sinsp_filter_check::parse_field_name(str); 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; } } #endif // HAS_FILTERING sysdig-0.1.87/userspace/libsinsp/filterchecks.h000066400000000000000000000270241237051215500215530ustar00rootroot00000000000000/* Copyright (C) 2013-2014 Draios inc. This file is part of sysdig. sysdig is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. sysdig is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with sysdig. If not, see . */ #pragma once #include #ifdef HAS_FILTERING #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); 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_filelds() { return &m_info; } // // Parse the name of the field. // Returns the lenght of the parsed field if successful, an exception in // case of error. // virtual int32_t parse_field_name(const char* str); // // If this check is used by a filter, extract the constant to compare it to // Doesn't return the field lenght 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::null; } // // 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; 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); int32_t extract_arg(string fldname, string val, OUT const struct ppm_param_info** parinfo, OUT int32_t* argid, OUT string* argname); 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_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) { 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_PORT = 9, TYPE_CLIENTPORT = 10, TYPE_SERVERPORT = 11, TYPE_L4PROTO = 12, TYPE_SOCKFAMILY = 13, TYPE_IS_SERVER = 14, }; 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(); int32_t parse_field_name(const char* str); 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 checks // 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_CMDLINE = 4, TYPE_CWD = 5, TYPE_NCHILDS = 6, TYPE_PPID = 7, TYPE_PNAME = 8, TYPE_APID = 9, TYPE_ANAME = 10, TYPE_LOGINSHELLID = 11, TYPE_DURATION = 12, TYPE_FDOPENCOUNT = 13, TYPE_FDLIMIT = 14, TYPE_FDUSAGE = 15, TYPE_VMSIZE = 16, TYPE_VMRSS = 17, TYPE_VMSWAP = 18, TYPE_PFMAJOR = 19, TYPE_PFMINOR = 20, TYPE_TID = 21, TYPE_ISMAINTHREAD = 22, TYPE_EXECTIME = 23, TYPE_TOTEXECTIME = 24, TYPE_IOBYTES = 25, TYPE_TOTIOBYTES = 26, TYPE_LATENCY = 27, TYPE_TOTLATENCY = 28, }; sinsp_filter_check_thread(); sinsp_filter_check* allocate_new(); int32_t parse_field_name(const char* str); 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); inline bool compare_full_apid(sinsp_evt *evt); bool compare_full_aname(sinsp_evt *evt); int32_t m_argid; uint32_t m_tbool; string m_tstr; uint64_t m_u64val; int64_t m_s64val; vector m_last_proc_switch_times; }; // // 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_DIR = 13, TYPE_TYPE = 14, TYPE_CPU = 15, TYPE_ARGS = 16, TYPE_ARGSTR = 17, TYPE_ARGRAW = 18, TYPE_INFO = 19, TYPE_BUFFER = 20, TYPE_RESSTR = 21, TYPE_RESRAW = 22, TYPE_FAILED = 23, TYPE_ISIO = 24, TYPE_ISIO_READ = 25, TYPE_ISIO_WRITE = 26, TYPE_IODIR = 27, TYPE_ISWAIT = 28, TYPE_ISSYSLOG = 29, TYPE_COUNT = 30, TYPE_AROUND = 31, }; sinsp_filter_check_event(); sinsp_filter_check* allocate_new(); int32_t parse_field_name(const char* str); 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_first_ts; uint64_t m_u64val; uint64_t m_tsdelta; uint32_t m_u32val; string m_strstorage; string m_argname; int32_t m_argid; 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 gmt2local(time_t t); void ts_to_string(uint64_t ts, OUT string* res, bool full, bool ns); bool m_is_compare; }; // // 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); 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); uint8_t* extract(sinsp_evt *evt, OUT uint32_t* len); sinsp_decoder_syslog* m_decoder; uint32_t m_gid; string m_name; }; #endif // HAS_FILTERING sysdig-0.1.87/userspace/libsinsp/ifinfo.cpp000066400000000000000000000116341237051215500207120ustar00rootroot00000000000000/* 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::to_string() { 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) { pipv4info->m_fields.m_sip = infer_ipv4_address(pipv4info->m_fields.m_dip); } else { pipv4info->m_fields.m_dip = infer_ipv4_address(pipv4info->m_fields.m_sip); } } 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 for the same subnet 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_interface(const sinsp_ipv4_ifinfo& ifinfo) { m_ipv4_interfaces.push_back(ifinfo); } 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) { m_ipv4_interfaces.clear(); m_ipv6_interfaces.clear(); import_ipv4_ifaddr_list(paddrlist->n_v4_addrs, paddrlist->v4list); import_ipv6_ifaddr_list(paddrlist->n_v6_addrs, paddrlist->v6list); } } vector* sinsp_network_interfaces::get_ipv4_list() { return &m_ipv4_interfaces; } vector* sinsp_network_interfaces::get_ipv6_list() { return &m_ipv6_interfaces; } sysdig-0.1.87/userspace/libsinsp/ifinfo.h000066400000000000000000000037661237051215500203660ustar00rootroot00000000000000/* 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(); uint32_t m_addr; uint32_t m_netmask; uint32_t m_bcast; string m_name; private: 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(); 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; };sysdig-0.1.87/userspace/libsinsp/ifinfo_test.cpp000066400000000000000000000075561237051215500217610ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/internal_metrics.cpp000066400000000000000000000016521237051215500230010ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/internal_metrics.h000066400000000000000000000051271237051215500224470ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/logger.cpp000066400000000000000000000067441237051215500207250ustar00rootroot00000000000000/* 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; } sysdig-0.1.87/userspace/libsinsp/logger.h000066400000000000000000000033601237051215500203610ustar00rootroot00000000000000/* 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 function that accepts printf syntax and returns the formatted buffer. char* format(severity sev, const char* fmt, ...); private: FILE* m_file; sinsp_logger_callback m_callback; uint32_t m_flags; severity m_sev; char m_tbuf[512]; }; sysdig-0.1.87/userspace/libsinsp/memmem.cpp000077500000000000000000000021531237051215500207140ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/parsers.cpp000066400000000000000000001731521237051215500211230ustar00rootroot00000000000000/* 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 #ifdef _WIN32 #include #else #include #include #ifdef _DEBUG #endif // _DEBUG #include #endif // _WIN32 #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; sinsp_parser::sinsp_parser(sinsp *inspector) : m_inspector(inspector), m_tmp_evt(m_inspector), m_fd_listener(NULL) { #if defined(HAS_CAPTURE) m_sysdig_pid = getpid(); #endif } sinsp_parser::~sinsp_parser() { for(uint32_t j = 0; j < m_protodecoders.size(); j++) { delete m_protodecoders[j]; } m_protodecoders.clear(); } /////////////////////////////////////////////////////////////////////////////// // PROCESSING ENTRY POINT /////////////////////////////////////////////////////////////////////////////// void sinsp_parser::process_event(sinsp_evt *evt) { uint16_t etype = evt->get_type(); bool is_live = m_inspector->is_live(); // // Cleanup the event-related state // reset(evt); // // When debug mode is not enabled, filter out events about sysdig itself // #if defined(HAS_CAPTURE) if(is_live && !m_inspector->is_debug_enabled()) { if(evt->get_tid() == m_sysdig_pid && etype != PPME_SCHEDSWITCH_1_E && etype != PPME_SCHEDSWITCH_6_E && 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) { 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: 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_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_EPOLLWAIT_E: parse_select_poll_epollwait_enter(evt); break; case PPME_CLONE_11_X: case PPME_CLONE_16_X: parse_clone_exit(evt); break; case PPME_SYSCALL_EXECVE_8_X: case PPME_SYSCALL_EXECVE_13_X: parse_execve_exit(evt); break; case PPME_PROCEXIT_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_ACCEPT4_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); 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; } } } /////////////////////////////////////////////////////////////////////////////// // 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(etype >= PPME_SCHEDSWITCH_1_E && etype <= PPME_DROP_X) { 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_CLONE_11_X || etype == PPME_CLONE_16_X || etype == PPME_SCHEDSWITCH_1_E || etype == PPME_SCHEDSWITCH_6_E) { query_os = false; } else { query_os = true; } evt->m_tinfo = evt->get_thread_info(query_os); if(etype == PPME_SCHEDSWITCH_1_E || etype == PPME_SCHEDSWITCH_6_E) { return false; } if(!evt->m_tinfo) { if(etype == PPME_CLONE_11_X || etype == PPME_CLONE_16_X) { #ifdef GATHER_INTERNAL_STATS m_inspector->m_thread_manager->m_failed_lookups->decrement(); #endif } else { ASSERT(false); } return false; } 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_tinfo->m_latency = 0; evt->m_tinfo->m_last_latency_entertime = evt->get_ts(); } else { // // event latency // if(evt->m_tinfo->m_last_latency_entertime != 0) { evt->m_tinfo->m_latency = evt->get_ts() - evt->m_tinfo->m_last_latency_entertime; ASSERT((int64_t)evt->m_tinfo->m_latency >= 0); } if(etype == evt->m_tinfo->m_lastevent_type + 1) { evt->m_tinfo->set_lastevent_data_validity(true); } else { evt->m_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 = evt->m_tinfo->get_fd(evt->m_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 = evt->m_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 = evt->m_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; } evt->m_tinfo->store_event(evt); #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()) { // // 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; unordered_map::iterator it; bool is_inverted_clone = false; // true if clone() in the child returns before the one in the parent bool tid_collision = false; // // 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; if(childtid < 0) { // // clone() failed. Do nothing and keep going. // return; } else 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. // In both cases, we create the thread entry here is_inverted_clone = true; // // The tid to add is the one that generated this event // childtid = tid; // // Get the flags, and check if this is a process or a new thread // switch(evt->get_type()) { case PPME_CLONE_11_X: parinfo = evt->get_param(8); break; case PPME_CLONE_16_X: parinfo = evt->get_param(13); break; default: ASSERT(false); } ASSERT(parinfo->m_len == sizeof(int32_t)); uint32_t flags = *(int32_t *)parinfo->m_val; 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 { 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; } // // 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 { ASSERT(false); 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; // 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 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 switch(evt->get_type()) { case PPME_CLONE_11_X: parinfo = evt->get_param(8); break; case PPME_CLONE_16_X: parinfo = evt->get_param(13); break; default: ASSERT(false); } ASSERT(parinfo->m_len == sizeof(int32_t)); tinfo.m_flags = *(int32_t *)parinfo->m_val; // // 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 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; if(evt->get_type() == PPME_CLONE_16_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; } // Copy the uid switch(evt->get_type()) { case PPME_CLONE_11_X: parinfo = evt->get_param(9); break; case PPME_CLONE_16_X: parinfo = evt->get_param(14); break; default: ASSERT(false); } ASSERT(parinfo->m_len == sizeof(int32_t)); tinfo.m_uid = *(int32_t *)parinfo->m_val; // Copy the uid switch(evt->get_type()) { case PPME_CLONE_11_X: parinfo = evt->get_param(10); break; case PPME_CLONE_16_X: parinfo = evt->get_param(15); break; default: ASSERT(false); } ASSERT(parinfo->m_len == sizeof(int32_t)); tinfo.m_gid = *(int32_t *)parinfo->m_val; // // 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) { evt->m_tinfo = NULL; evt->m_tinfo = evt->get_thread_info(); } return; } void sinsp_parser::parse_execve_exit(sinsp_evt *evt) { sinsp_evt_param *parinfo; int64_t retval; // 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; } string prev_comm(evt->m_tinfo->m_comm); string prev_exe(evt->m_tinfo->m_exe); // Get the command name parinfo = evt->get_param(1); string tmps = parinfo->m_val; tmps = tmps.substr(tmps.rfind("/") + 1); evt->m_tinfo->m_comm = tmps; // // XXX We should retrieve the full executable name from the arguments that execve receives in the kernel, // but for the moment we don't do it, so we just copy the command name into the exe string // evt->m_tinfo->m_exe = parinfo->m_val; // 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; if(evt->get_type() == PPME_SYSCALL_EXECVE_13_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; } // // 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 = 0; 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; // // execve potentially breaks the program chain, and so we need to reflect it in our parents program count. // if((prev_comm != evt->m_tinfo->m_comm) || (prev_exe != evt->m_tinfo->m_exe)) { if(evt->m_tinfo->m_progid != -1LL) { m_inspector->m_thread_manager->decrement_program_childcount(evt->m_tinfo); } else { m_inspector->m_thread_manager->increment_program_childcount(evt->m_tinfo, 0, 0); } } #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 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; // uint32_t mode; 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) { const char *parstr; if(evt->m_fdinfo == NULL) { return; } // // 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 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); ASSERT(evt->m_fdinfo != NULL); } 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; } //m_inspector->push_fdop(tid, evt->m_fdinfo, sinsp_fdop(fd, evt->get_type())); // // 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()); 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, data, (uint32_t)retval, datalen); } // // Call the protocol decoder callbacks associated to this event // vector* cbacks = &(evt->m_fdinfo->m_read_callbacks); vector::iterator it; for(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()); 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, data, (uint32_t)retval, datalen); } // // Call the protocol decoder callbacks associated to this event // vector* cbacks = &(evt->m_fdinfo->m_write_callbacks); vector::iterator it; for(it = cbacks->begin(); it != cbacks->end(); ++it) { (*it)->on_write(evt, data, datalen); } } } } 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; // // 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; } // // 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_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; } *(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)); parinfo = evt->get_param(3); evt->m_tinfo->m_vmsize_kb = *(uint32_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(uint32_t)); parinfo = evt->get_param(4); evt->m_tinfo->m_vmrss_kb = *(uint32_t *)parinfo->m_val; ASSERT(parinfo->m_len == sizeof(uint32_t)); parinfo = evt->get_param(5); evt->m_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)); } } sysdig-0.1.87/userspace/libsinsp/parsers.h000066400000000000000000000106111237051215500205560ustar00rootroot00000000000000/* 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 sinsp_parser { public: sinsp_parser(sinsp* inspector); ~sinsp_parser(); // // Processing entry point // void process_event(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); // // 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_enter(sinsp_evt* evt); void parse_rw_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); 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); // // Pointers to inspector context // sinsp* m_inspector; #if defined(HAS_CAPTURE) int64_t m_sysdig_pid; #endif // // 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; friend class sinsp_analyzer; friend class sinsp_analyzer_fd_listener; friend class sinsp_protodecoder; }; sysdig-0.1.87/userspace/libsinsp/procinfo_test.cpp000066400000000000000000000100521237051215500223070ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/protodecoder.cpp000066400000000000000000000150471237051215500221330ustar00rootroot00000000000000/* 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) { ASSERT(fdinfo != NULL); 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->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->m_name.find("/dev/log") != string::npos) { register_write_callback(fdinfo); } else { 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.1.87/userspace/libsinsp/protodecoder.h000066400000000000000000000076711237051215500216040ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/settings.h000066400000000000000000000040341237051215500207410ustar00rootroot00000000000000/* 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_STORAGE_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 65536 // // 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 600 // // 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 // // 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.1.87/userspace/libsinsp/sinsp.cpp000066400000000000000000000442151237051215500205750ustar00rootroot00000000000000/* 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 #endif // _WIN32 #include "sinsp.h" #include "sinsp_int.h" #include "filter.h" #include "filterchecks.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 /////////////////////////////////////////////////////////////////////////////// // sinsp implementation /////////////////////////////////////////////////////////////////////////////// sinsp::sinsp() : m_evt(this) { m_h = NULL; m_parser = NULL; m_dumper = 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_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_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; m_isdropping = false; m_n_proc_lookups = 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_filesize = -1; } 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; } } void sinsp::add_protodecoders() { m_parser->add_protodecoder("syslog"); } 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 // add_protodecoders(); // // Reset the thread manager // m_thread_manager->clear(); // // Allocate the cycle writer // m_cycle_writer = new cycle_writer(); // // Basic inits // #ifdef GATHER_INTERNAL_STATS m_stats.clear(); #endif 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; import_ifaddr_list(); import_thread_table(); import_user_list(); #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); } } void sinsp::open(uint32_t timeout_ms) { char error[SCAP_LASTERR_SIZE]; g_logger.log("starting live capture"); m_islive = true; m_h = scap_open_live(error); if(m_h == NULL) { throw sinsp_exception(error); } init(); } void sinsp::open(string filename) { char error[SCAP_LASTERR_SIZE]; m_islive = false; if(filename == "") { open(); return; } m_input_filename = filename; g_logger.log("starting offline capture"); m_h = scap_open_offline(filename.c_str(), error); if(m_h == NULL) { throw sinsp_exception(error); } // // gianluca: This might need to be replaced with // a portable stat(), since I'm afraid that on S3 // (that we'll use in the backend) the seek will // read the entire file anyway // FILE* fp = fopen(filename.c_str(), "rb"); if(fp) { fseek(fp, 0L, SEEK_END); m_filesize = ftell(fp); fclose(fp); } 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)); } } 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::import_thread_table() { scap_threadinfo *pi; scap_threadinfo *tpi; sinsp_threadinfo newti(this); 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) { newti.init(pi); m_thread_manager->add_thread(newti, true); } // // 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(); } 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); 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); } bool should_drop(sinsp_evt *evt, bool* stopped, bool* switched); int32_t sinsp::next(OUT sinsp_evt **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(&m_evt); } m_decoders_reset_list.clear(); } // // Get the event from libscap // int32_t res = scap_next(m_h, &(m_evt.m_pevt), &(m_evt.m_cpuid)); // The number of bytes to consider in the dumper int32_t bytes_to_write; if(res != SCAP_SUCCESS) { if(res == SCAP_TIMEOUT) { *evt = NULL; return res; } else if(res == SCAP_EOF) { #ifdef HAS_ANALYZER if(m_analyzer) { m_analyzer->process_event(NULL, sinsp_analyzer::DF_NONE); } #endif } else { throw sinsp_exception(scap_getlasterr(m_h)); } return res; } // // Store a couple of values that we'll need later inside the event. // m_evt.m_evtnum = get_num_events(); m_lastevent_ts = m_evt.get_ts(); #ifdef HAS_FILTERING if(m_firstevent_ts == 0) { m_firstevent_ts = m_lastevent_ts; } #endif #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 // m_thread_manager->remove_inactive_threads(); #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(&m_evt, &m_isdropping, &sw); #endif // // Run the state engine // #ifdef SIMULATE_DROP_MODE if(!sd || m_isdropping) { m_parser->process_event(&m_evt); } if(sd && !m_isdropping) { *evt = NULL; return SCAP_TIMEOUT; } #else m_parser->process_event(&m_evt); #endif // // If needed, dump the event to file // if(NULL != m_dumper) { #if defined(HAS_FILTERING) && defined(HAS_CAPTURE_FILTERING) scap_dump_flags dflags; bool do_drop; dflags = m_evt.get_dump_flags(&do_drop); if(do_drop) { *evt = &m_evt; return SCAP_TIMEOUT; } #endif if(m_write_cycling) { res = scap_number_of_bytes_to_write(m_evt.m_pevt, m_evt.m_cpuid, &bytes_to_write); if(SCAP_SUCCESS != res) { throw sinsp_exception(scap_getlasterr(m_h)); } else { switch(m_cycle_writer->consider(bytes_to_write)) { 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, m_evt.m_pevt, m_evt.m_cpuid, dflags); if(SCAP_SUCCESS != res) { throw sinsp_exception(scap_getlasterr(m_h)); } } #if defined(HAS_FILTERING) && defined(HAS_CAPTURE_FILTERING) if(m_evt.m_filtered_out) { *evt = &m_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(&m_evt, sinsp_analyzer::DF_FORCE_FLUSH); } else if(sw) { m_analyzer->process_event(&m_evt, sinsp_analyzer::DF_FORCE_FLUSH_BUT_DONT_EMIT); } else { m_analyzer->process_event(&m_evt, sinsp_analyzer::DF_FORCE_NOFLUSH); } } #else // SIMULATE_DROP_MODE m_analyzer->process_event(&m_evt, sinsp_analyzer::DF_NONE); #endif // SIMULATE_DROP_MODE } #endif // // Update the last event time for this thread // if(m_evt.m_tinfo && m_evt.get_type() != PPME_SCHEDSWITCH_1_E && m_evt.get_type() != PPME_SCHEDSWITCH_6_E) { m_evt.m_tinfo->m_prevevent_ts = m_evt.m_tinfo->m_lastevent_ts; m_evt.m_tinfo->m_lastevent_ts = m_lastevent_ts; } // // Done // *evt = &m_evt; return res; } uint64_t sinsp::get_num_events() { return scap_event_get_num(m_h); } sinsp_threadinfo* sinsp::get_thread(int64_t tid, bool query_os_if_not_found, bool lookup_only) { sinsp_threadinfo* sinsp_proc = m_thread_manager->get_thread(tid, lookup_only); if(sinsp_proc == NULL && query_os_if_not_found) { sinsp_threadinfo newti(this); scap_threadinfo* scap_proc = NULL; 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"); } if(m_n_proc_lookups == m_max_n_proc_lookups) { g_logger.format(sinsp_logger::SEV_INFO, "Reached max processs lookup number"); } 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 = true; 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 = false; } scap_proc = scap_proc_get(m_h, tid, scan_sockets); } 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); sinsp_proc = m_thread_manager->get_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); } 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(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) { 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); } 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; } bool sinsp::is_live() { return m_islive; } void sinsp::set_debug_mode(bool enable_debug) { m_isdebug_enabled = enable_debug; } void sinsp::set_fatfile_dump_mode(bool enable_fatfile) { m_isfatfile_enabled = enable_fatfile; } bool sinsp::is_debug_enabled() { return m_isdebug_enabled; } 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, bool do_cycle, bool compress) { m_compress = compress; if(rollover_mb != 0 || duration_seconds != 0 || file_limit != 0 || do_cycle == true) { m_write_cycling = true; } return m_cycle_writer->setup(base_file_name, rollover_mb, duration_seconds, file_limit, do_cycle); } 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; } sysdig-0.1.87/userspace/libsinsp/sinsp.h000066400000000000000000000403511237051215500202370ustar00rootroot00000000000000/* 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) #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 "chisel.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; vector sinsp_split(const string &s, char delim); /*! \brief Information about a group of filter/formatting fields. */ class filter_check_info { public: string m_name; ///< Field class name. int32_t m_nfiedls; ///< Number of fields in this field group. const filtercheck_field_info* m_fields; ///< Array containing m_nfiedls field descriptions. }; /*! \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 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" #define DEFAULT_OUTPUT_STR "*%evt.time %evt.cpu %proc.name (%thread.tid) %evt.dir %evt.type %evt.args" /** @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 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 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. */ bool is_live(); /*! \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 Returns true if the debug mode is enabled. */ bool is_debug_enabled(); /*! \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 XXX. */ double get_read_progress(); // // Misc internal stuff // void stop_dropping_mode(); void start_dropping_mode(uint32_t sampling_ratio); void import_ipv4_interface(const sinsp_ipv4_ifinfo& ifinfo); // // 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, bool do_cycle, bool compress); 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); scap_t* m_h; int64_t m_filesize; bool m_islive; string m_input_filename; bool m_isdebug_enabled; bool m_isfatfile_enabled; 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; 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; #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; uint32_t m_max_n_proc_lookups; uint32_t m_max_n_proc_socket_lookups; // // Saved snaplen // uint32_t m_snaplen; // // Some thread table limits // uint32_t m_max_thread_table_size; uint64_t m_thread_timeout_ns; uint64_t m_inactive_thread_scan_time_ns; // // How to render the data buffers // sinsp_evt::param_fmt m_buffer_format; // // User and group tables // unordered_map m_userlist; unordered_map m_grouplist; // // The cycle-writer for files // cycle_writer* m_cycle_writer; bool m_write_cycling; // // Some dropping infrastructure // bool m_isdropping; // // Protocol decoding state // vector m_decoders_reset_list; 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_dumper; friend class sinsp_analyzer_fd_listener; friend class sinsp_chisel; friend class sinsp_protodecoder; friend class lua_cbacks; template friend class sinsp_connection_manager; }; /*@}*/ sysdig-0.1.87/userspace/libsinsp/sinsp.vcxproj000066400000000000000000000163251237051215500215070ustar00rootroot00000000000000 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.1.87/userspace/libsinsp/sinsp.vcxproj.filters000066400000000000000000000100121237051215500231410ustar00rootroot00000000000000 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.1.87/userspace/libsinsp/sinsp_errno.h000066400000000000000000000226111237051215500214430ustar00rootroot00000000000000/* 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_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.1.87/userspace/libsinsp/sinsp_int.h000066400000000000000000000064271237051215500211170ustar00rootroot00000000000000/* 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 #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->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, char *data, uint32_t original_len, uint32_t len) = 0; virtual void on_write(sinsp_evt *evt, int64_t tid, int64_t fd, char *data, uint32_t original_len, 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.1.87/userspace/libsinsp/sinsp_signal.h000066400000000000000000000031751237051215500215770ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/sinsp_test.cpp000066400000000000000000000020441237051215500216260ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/stats.cpp000066400000000000000000000051171237051215500205750ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/stats.h000066400000000000000000000027771237051215500202530ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/third-party/000077500000000000000000000000001237051215500211765ustar00rootroot00000000000000sysdig-0.1.87/userspace/libsinsp/third-party/jsoncpp/000077500000000000000000000000001237051215500226525ustar00rootroot00000000000000sysdig-0.1.87/userspace/libsinsp/third-party/jsoncpp/json/000077500000000000000000000000001237051215500236235ustar00rootroot00000000000000sysdig-0.1.87/userspace/libsinsp/third-party/jsoncpp/json/json-forwards.h000066400000000000000000000211701237051215500265730ustar00rootroot00000000000000/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/). /// It is intented to be used with #include /// 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_AMALGATED // ////////////////////////////////////////////////////////////////////// // 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 defined, indicates that Json specific container should be used /// (hash table & simple deque container with customizable allocator). /// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332 //# define JSON_VALUE_USE_INTERNAL_MAP 1 /// Force usage of standard new/malloc based allocator instead of memory pool based allocator. /// The memory pools allocator used optimization (initializing Value and ValueInternalLink /// as if it was a POD) that may cause some validation tool to report errors. /// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. //# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 /// If defined, indicates that Json use exception to report invalid type manipulation /// instead of C assert macro. # define JSON_USE_EXCEPTION 1 /// 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) # define JSON_API __declspec(dllexport) # elif defined(JSON_DLL) # define JSON_API __declspec(dllimport) # else # 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 #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)) #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; #ifdef JSON_VALUE_USE_INTERNAL_MAP class ValueMapAllocator; class ValueInternalLink; class ValueInternalArray; class ValueInternalMap; #endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP } // 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.1.87/userspace/libsinsp/third-party/jsoncpp/json/json.h000066400000000000000000001704261237051215500247570ustar00rootroot00000000000000/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/). /// It is intented to be used with #include // ////////////////////////////////////////////////////////////////////// // 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 // ////////////////////////////////////////////////////////////////////// #define JSON_IS_AMALGAMATION #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_AMALGATED // ////////////////////////////////////////////////////////////////////// // 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 defined, indicates that Json specific container should be used /// (hash table & simple deque container with customizable allocator). /// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332 //# define JSON_VALUE_USE_INTERNAL_MAP 1 /// Force usage of standard new/malloc based allocator instead of memory pool based allocator. /// The memory pools allocator used optimization (initializing Value and ValueInternalLink /// as if it was a POD) that may cause some validation tool to report errors. /// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. //# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 /// If defined, indicates that Json use exception to report invalid type manipulation /// instead of C assert macro. # define JSON_USE_EXCEPTION 1 /// 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) # define JSON_API __declspec(dllexport) # elif defined(JSON_DLL) # define JSON_API __declspec(dllimport) # else # 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 #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)) #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; #ifdef JSON_VALUE_USE_INTERNAL_MAP class ValueMapAllocator; class ValueInternalLink; class ValueInternalArray; class ValueInternalMap; #endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP } // 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 # ifndef JSON_USE_CPPTL_SMALLMAP # include # else # include # endif # ifdef JSON_USE_CPPTL # include # endif /** \brief JSON (JavaScript Object Notation). */ namespace Json { /** \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 ) : str_( czstring ) { } operator const char *() const { return str_; } const char *c_str() const { return str_; } private: const char *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 resize and initialized * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. * * The get() methods can be used to obtanis 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. */ class JSON_API Value { friend class ValueIteratorBase; # ifdef JSON_VALUE_USE_INTERNAL_MAP friend class ValueInternalLink; friend class ValueInternalMap; # endif 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 null; /// 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; /// 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; private: #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION # ifndef JSON_VALUE_USE_INTERNAL_MAP class CZString { public: enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy }; CZString( ArrayIndex index ); CZString( const char *cstr, DuplicationPolicy allocate ); CZString( const CZString &other ); ~CZString(); CZString &operator =( const CZString &other ); bool operator<( const CZString &other ) const; bool operator==( const CZString &other ) const; ArrayIndex index() const; const char *c_str() const; bool isStaticString() const; private: void swap( CZString &other ); const char *cstr_; ArrayIndex index_; }; public: # ifndef JSON_USE_CPPTL_SMALLMAP typedef std::map ObjectValues; # else typedef CppTL::SmallMap ObjectValues; # endif // ifndef JSON_USE_CPPTL_SMALLMAP # endif // ifndef JSON_VALUE_USE_INTERNAL_MAP #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 ); Value( const char *beginValue, const char *endValue ); /** \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. * Example of usage: * \code * Json::Value aValue( StaticString("some text") ); * \endcode */ Value( const StaticString &value ); Value( const std::string &value ); # ifdef JSON_USE_CPPTL Value( const CppTL::ConstString &value ); # endif Value( bool value ); Value( const Value &other ); ~Value(); Value &operator=( const Value &other ); /// Swap values. /// \note Currently, comments are intentionally not swapped, for /// both logic and efficiency. void swap( Value &other ); ValueType type() 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; bool operator !=( const Value &other ) const; int compare( const Value &other ) const; const char *asCString() const; std::string asString() const; # ifdef JSON_USE_CPPTL CppTL::ConstString asConstString() const; # endif Int asInt() const; UInt asUInt() const; Int64 asInt64() const; UInt64 asUInt64() const; 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 isUInt() 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. 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. Value &operator[]( const std::string &key ); /// Access an object value by name, returns null if there is no member with that name. 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 as 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. Value get( const char *key, const Value &defaultValue ) const; /// Return the member named key if it exist, defaultValue otherwise. Value get( const std::string &key, const Value &defaultValue ) const; # ifdef JSON_USE_CPPTL /// Return the member named key if it exist, defaultValue otherwise. Value get( const CppTL::ConstString &key, const Value &defaultValue ) const; # endif /// \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 Value removeMember( const char* key ); /// Same as removeMember(const char*) Value removeMember( const std::string &key ); /// Return true if the object has a member named key. bool isMember( const char *key ) const; /// Return true if the object has a member named key. bool isMember( const std::string &key ) 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 /// Comments must be //... or /* ... */ void setComment( const char *comment, 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: Value &resolveReference( const char *key, bool isStatic ); # ifdef JSON_VALUE_USE_INTERNAL_MAP inline bool isItemAvailable() const { return itemIsUsed_ == 0; } inline void setItemUsed( bool isUsed = true ) { itemIsUsed_ = isUsed ? 1 : 0; } inline bool isMemberNameStatic() const { return memberNameIsStatic_ == 0; } inline void setMemberNameIsStatic( bool isStatic ) { memberNameIsStatic_ = isStatic ? 1 : 0; } # endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP private: struct CommentInfo { CommentInfo(); ~CommentInfo(); void setComment( const char *text ); 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_; # ifdef JSON_VALUE_USE_INTERNAL_MAP ValueInternalArray *array_; ValueInternalMap *map_; #else ObjectValues *map_; # endif } value_; ValueType type_ : 8; int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. # ifdef JSON_VALUE_USE_INTERNAL_MAP unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. # endif CommentInfo *comments_; }; /** \brief Experimental and untested: represents an element of the "path" to access a node. */ class 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 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_; }; #ifdef JSON_VALUE_USE_INTERNAL_MAP /** \brief Allocator to customize Value internal map. * Below is an example of a simple implementation (default implementation actually * use memory pool for speed). * \code class DefaultValueMapAllocator : public ValueMapAllocator { public: // overridden from ValueMapAllocator virtual ValueInternalMap *newMap() { return new ValueInternalMap(); } virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) { return new ValueInternalMap( other ); } virtual void destructMap( ValueInternalMap *map ) { delete map; } virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) { return new ValueInternalLink[size]; } virtual void releaseMapBuckets( ValueInternalLink *links ) { delete [] links; } virtual ValueInternalLink *allocateMapLink() { return new ValueInternalLink(); } virtual void releaseMapLink( ValueInternalLink *link ) { delete link; } }; * \endcode */ class JSON_API ValueMapAllocator { public: virtual ~ValueMapAllocator(); virtual ValueInternalMap *newMap() = 0; virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0; virtual void destructMap( ValueInternalMap *map ) = 0; virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0; virtual void releaseMapBuckets( ValueInternalLink *links ) = 0; virtual ValueInternalLink *allocateMapLink() = 0; virtual void releaseMapLink( ValueInternalLink *link ) = 0; }; /** \brief ValueInternalMap hash-map bucket chain link (for internal use only). * \internal previous_ & next_ allows for bidirectional traversal. */ class JSON_API ValueInternalLink { public: enum { itemPerLink = 6 }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. enum InternalFlags { flagAvailable = 0, flagUsed = 1 }; ValueInternalLink(); ~ValueInternalLink(); Value items_[itemPerLink]; char *keys_[itemPerLink]; ValueInternalLink *previous_; ValueInternalLink *next_; }; /** \brief A linked page based hash-table implementation used internally by Value. * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked * list in each bucket to handle collision. There is an addional twist in that * each node of the collision linked list is a page containing a fixed amount of * value. This provides a better compromise between memory usage and speed. * * Each bucket is made up of a chained list of ValueInternalLink. The last * link of a given bucket can be found in the 'previous_' field of the following bucket. * The last link of the last bucket is stored in tailLink_ as it has no following bucket. * Only the last link of a bucket may contains 'available' item. The last link always * contains at least one element unless is it the bucket one very first link. */ class JSON_API ValueInternalMap { friend class ValueIteratorBase; friend class Value; public: typedef unsigned int HashKey; typedef unsigned int BucketIndex; # ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION struct IteratorState { IteratorState() : map_(0) , link_(0) , itemIndex_(0) , bucketIndex_(0) { } ValueInternalMap *map_; ValueInternalLink *link_; BucketIndex itemIndex_; BucketIndex bucketIndex_; }; # endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION ValueInternalMap(); ValueInternalMap( const ValueInternalMap &other ); ValueInternalMap &operator =( const ValueInternalMap &other ); ~ValueInternalMap(); void swap( ValueInternalMap &other ); BucketIndex size() const; void clear(); bool reserveDelta( BucketIndex growth ); bool reserve( BucketIndex newItemCount ); const Value *find( const char *key ) const; Value *find( const char *key ); Value &resolveReference( const char *key, bool isStatic ); void remove( const char *key ); void doActualRemove( ValueInternalLink *link, BucketIndex index, BucketIndex bucketIndex ); ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex ); Value &setNewItem( const char *key, bool isStatic, ValueInternalLink *link, BucketIndex index ); Value &unsafeAdd( const char *key, bool isStatic, HashKey hashedKey ); HashKey hash( const char *key ) const; int compare( const ValueInternalMap &other ) const; private: void makeBeginIterator( IteratorState &it ) const; void makeEndIterator( IteratorState &it ) const; static bool equals( const IteratorState &x, const IteratorState &other ); static void increment( IteratorState &iterator ); static void incrementBucket( IteratorState &iterator ); static void decrement( IteratorState &iterator ); static const char *key( const IteratorState &iterator ); static const char *key( const IteratorState &iterator, bool &isStatic ); static Value &value( const IteratorState &iterator ); static int distance( const IteratorState &x, const IteratorState &y ); private: ValueInternalLink *buckets_; ValueInternalLink *tailLink_; BucketIndex bucketsSize_; BucketIndex itemCount_; }; /** \brief A simplified deque implementation used internally by Value. * \internal * It is based on a list of fixed "page", each page contains a fixed number of items. * Instead of using a linked-list, a array of pointer is used for fast item look-up. * Look-up for an element is as follow: * - compute page index: pageIndex = itemIndex / itemsPerPage * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] * * Insertion is amortized constant time (only the array containing the index of pointers * need to be reallocated when items are appended). */ class JSON_API ValueInternalArray { friend class Value; friend class ValueIteratorBase; public: enum { itemsPerPage = 8 }; // should be a power of 2 for fast divide and modulo. typedef Value::ArrayIndex ArrayIndex; typedef unsigned int PageIndex; # ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION struct IteratorState // Must be a POD { IteratorState() : array_(0) , currentPageIndex_(0) , currentItemIndex_(0) { } ValueInternalArray *array_; Value **currentPageIndex_; unsigned int currentItemIndex_; }; # endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION ValueInternalArray(); ValueInternalArray( const ValueInternalArray &other ); ValueInternalArray &operator =( const ValueInternalArray &other ); ~ValueInternalArray(); void swap( ValueInternalArray &other ); void clear(); void resize( ArrayIndex newSize ); Value &resolveReference( ArrayIndex index ); Value *find( ArrayIndex index ) const; ArrayIndex size() const; int compare( const ValueInternalArray &other ) const; private: static bool equals( const IteratorState &x, const IteratorState &other ); static void increment( IteratorState &iterator ); static void decrement( IteratorState &iterator ); static Value &dereference( const IteratorState &iterator ); static Value &unsafeDereference( const IteratorState &iterator ); static int distance( const IteratorState &x, const IteratorState &y ); static ArrayIndex indexOf( const IteratorState &iterator ); void makeBeginIterator( IteratorState &it ) const; void makeEndIterator( IteratorState &it ) const; void makeIterator( IteratorState &it, ArrayIndex index ) const; void makeIndexValid( ArrayIndex index ); Value **pages_; ArrayIndex size_; PageIndex pageCount_; }; /** \brief Experimental: do not use. Allocator to customize Value internal array. * Below is an example of a simple implementation (actual implementation use * memory pool). \code class DefaultValueArrayAllocator : public ValueArrayAllocator { public: // overridden from ValueArrayAllocator virtual ~DefaultValueArrayAllocator() { } virtual ValueInternalArray *newArray() { return new ValueInternalArray(); } virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) { return new ValueInternalArray( other ); } virtual void destruct( ValueInternalArray *array ) { delete array; } virtual void reallocateArrayPageIndex( Value **&indexes, ValueInternalArray::PageIndex &indexCount, ValueInternalArray::PageIndex minNewIndexCount ) { ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; if ( minNewIndexCount > newIndexCount ) newIndexCount = minNewIndexCount; void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); if ( !newIndexes ) throw std::bad_alloc(); indexCount = newIndexCount; indexes = static_cast( newIndexes ); } virtual void releaseArrayPageIndex( Value **indexes, ValueInternalArray::PageIndex indexCount ) { if ( indexes ) free( indexes ); } virtual Value *allocateArrayPage() { return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); } virtual void releaseArrayPage( Value *value ) { if ( value ) free( value ); } }; \endcode */ class JSON_API ValueArrayAllocator { public: virtual ~ValueArrayAllocator(); virtual ValueInternalArray *newArray() = 0; virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0; virtual void destructArray( ValueInternalArray *array ) = 0; /** \brief Reallocate array page index. * Reallocates an array of pointer on each page. * \param indexes [input] pointer on the current index. May be \c NULL. * [output] pointer on the new index of at least * \a minNewIndexCount pages. * \param indexCount [input] current number of pages in the index. * [output] number of page the reallocated index can handle. * \b MUST be >= \a minNewIndexCount. * \param minNewIndexCount Minimum number of page the new index must be able to * handle. */ virtual void reallocateArrayPageIndex( Value **&indexes, ValueInternalArray::PageIndex &indexCount, ValueInternalArray::PageIndex minNewIndexCount ) = 0; virtual void releaseArrayPageIndex( Value **indexes, ValueInternalArray::PageIndex indexCount ) = 0; virtual Value *allocateArrayPage() = 0; virtual void releaseArrayPage( Value *value ) = 0; }; #endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP /** \brief base class for Value iterators. * */ class ValueIteratorBase { public: typedef unsigned int size_t; typedef int difference_type; typedef ValueIteratorBase SelfType; ValueIteratorBase(); #ifndef JSON_VALUE_USE_INTERNAL_MAP explicit ValueIteratorBase( const Value::ObjectValues::iterator ¤t ); #else ValueIteratorBase( const ValueInternalArray::IteratorState &state ); ValueIteratorBase( const ValueInternalMap::IteratorState &state ); #endif 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 computeDistance( other ); } /// 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. -1 if it is not an arrayValue. UInt index() const; /// Return the member name of the referenced Value. "" if it is not an objectValue. const char *memberName() 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: #ifndef JSON_VALUE_USE_INTERNAL_MAP Value::ObjectValues::iterator current_; // Indicates that iterator is for a null value. bool isNull_; #else union { ValueInternalArray::IteratorState array_; ValueInternalMap::IteratorState map_; } iterator_; bool isArray_; #endif }; /** \brief const iterator for object and array value. * */ class ValueConstIterator : public ValueIteratorBase { friend class Value; public: 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. */ #ifndef JSON_VALUE_USE_INTERNAL_MAP explicit ValueConstIterator( const Value::ObjectValues::iterator ¤t ); #else ValueConstIterator( const ValueInternalArray::IteratorState &state ); ValueConstIterator( const ValueInternalMap::IteratorState &state ); #endif 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(); } }; /** \brief Iterator for object and array value. */ class ValueIterator : public ValueIteratorBase { friend class Value; public: 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. */ #ifndef JSON_VALUE_USE_INTERNAL_MAP explicit ValueIterator( const Value::ObjectValues::iterator ¤t ); #else ValueIterator( const ValueInternalArray::IteratorState &state ); ValueIterator( const ValueInternalMap::IteratorState &state ); #endif 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(); } }; } // namespace Json #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 namespace Json { /** \brief Unserialize a JSON document into a Value. * */ 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 expectToken( TokenType type, Token &token, const char *message ); 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 decodeString( Token &token ); bool decodeString( Token &token, std::string &decoded ); bool decodeDouble( Token &token ); bool decodeUnicodeCodePoint( Token &token, Location ¤t, Location end, unsigned int &unicode ); bool decodeUnicodeEscapeSequence( Token &token, Location ¤t, 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 ¤tValue(); 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_; }; /** \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<<() */ std::istream& operator>>( std::istream&, Value& ); } // namespace Json #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 namespace Json { class Value; /** \brief Abstract class for writers. */ 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 */ 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() */ 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() */ 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_; }; # 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>>() std::ostream& operator<<( std::ostream&, const Value &root ); } // namespace Json #endif // JSON_WRITER_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: include/json/writer.h // ////////////////////////////////////////////////////////////////////// #endif //ifndef JSON_AMALGATED_H_INCLUDED sysdig-0.1.87/userspace/libsinsp/third-party/jsoncpp/jsoncpp.cpp000066400000000000000000003127121237051215500250400ustar00rootroot00000000000000/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/). /// It is intented to be used with #include // ////////////////////////////////////////////////////////////////////// // 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 // ////////////////////////////////////////////////////////////////////// // 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 *¤t ) { *--current = 0; do { *--current = char(value % 10) + '0'; value /= 10; } while ( value != 0 ); } } // 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-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 #if !defined(JSON_IS_AMALGAMATION) # include # include # include "json_tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include #include #include #include #include #if _MSC_VER >= 1400 // VC++ 8.0 #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. #endif namespace Json { // 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 inline bool in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) { return c == c1 || c == c2 || c == c3 || c == c4; } static inline bool in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) { return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; } 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() : features_( Features::all() ) { } Reader::Reader( const Features &features ) : features_( features ) { } 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 ); 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() { 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: currentValue() = true; break; case tokenFalse: currentValue() = false; break; case tokenNull: currentValue() = Value(); break; default: return addError( "Syntax error: value, object or array expected.", token ); } if ( collectComments_ ) { lastValueEnd_ = current_; lastValue_ = ¤tValue(); } return successful; } void Reader::skipCommentTokens( Token &token ) { if ( features_.allowComments_ ) { do { readToken( token ); } while ( token.type_ == tokenComment ); } else { readToken( token ); } } bool Reader::expectToken( TokenType type, Token &token, const char *message ) { readToken( token ); if ( token.type_ != type ) return addError( message, token ); return true; } 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; } void Reader::addComment( Location begin, Location end, CommentPlacement placement ) { assert( collectComments_ ); if ( placement == commentAfterOnSameLine ) { assert( lastValue_ != 0 ); lastValue_->setComment( std::string( begin, end ), placement ); } else { if ( !commentsBefore_.empty() ) commentsBefore_ += "\n"; commentsBefore_ += std::string( begin, end ); } } 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 == '\r' || c == '\n' ) break; } return true; } void Reader::readNumber() { while ( current_ != end_ ) { if ( !(*current_ >= '0' && *current_ <= '9') && !in( *current_, '.', 'e', 'E', '+', '-' ) ) break; ++current_; } } 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; currentValue() = Value( objectValue ); 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; if ( tokenName.type_ != tokenString ) break; name = ""; if ( !decodeString( tokenName, name ) ) return recoverFromError( tokenObjectEnd ); 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*/ ) { currentValue() = Value( arrayValue ); 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 ) { bool isDouble = false; for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) { isDouble = isDouble || in( *inspect, '.', 'e', 'E', '+' ) || ( *inspect == '-' && inspect != token.start_ ); } if ( isDouble ) return decodeDouble( token ); // 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; Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) : Value::maxLargestUInt; Value::LargestUInt threshold = maxIntegerValue / 10; Value::UInt lastDigitThreshold = Value::UInt( maxIntegerValue % 10 ); assert( lastDigitThreshold >=0 && lastDigitThreshold <= 9 ); Value::LargestUInt value = 0; while ( current < token.end_ ) { Char c = *current++; if ( c < '0' || c > '9' ) return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); Value::UInt digit(c - '0'); if ( value >= threshold ) { // If the current digit is not the last one, or if it is // greater than the last digit of the maximum integer value, // the parse the number as a double. if ( current != token.end_ || digit > lastDigitThreshold ) { return decodeDouble( token ); } } value = value * 10 + digit; } if ( isNegative ) currentValue() = -Value::LargestInt( value ); else if ( value <= Value::LargestUInt(Value::maxInt) ) currentValue() = Value::LargestInt( value ); else currentValue() = value; return true; } bool Reader::decodeDouble( Token &token ) { double value = 0; const int bufferSize = 32; int count; int length = int(token.end_ - token.start_); if ( length <= bufferSize ) { Char buffer[bufferSize+1]; memcpy( buffer, token.start_, length ); buffer[length] = 0; count = sscanf( buffer, "%lf", &value ); } else { std::string buffer( token.start_, token.end_ ); count = sscanf( buffer.c_str(), "%lf", &value ); } if ( count != 1 ) return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); currentValue() = value; return true; } bool Reader::decodeString( Token &token ) { std::string decoded; if ( !decodeString( token, decoded ) ) return false; currentValue() = 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 ¤t, 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 ¤t, 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]; sprintf( buffer, "Line %d, Column %d", line, column ); return buffer; } // Deprecated. Preserved for backward compatibility std::string Reader::getFormatedErrorMessages() const { return getFormattedErrorMessages(); } std::string Reader::getFormattedErrorMessages() const { std::string formattedMessage; for ( Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError ) { const ErrorInfo &error = *itError; formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; formattedMessage += " " + error.message_ + "\n"; if ( error.extra_ ) formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; } return formattedMessage; } std::istream& operator>>( std::istream &sin, Value &root ) { Json::Reader reader; bool ok = reader.parse(sin, root, true); //JSON_ASSERT( ok ); if (!ok) throw std::runtime_error(reader.getFormattedErrorMessages()); return sin; } } // namespace Json // ////////////////////////////////////////////////////////////////////// // End of content of file: src/lib_json/json_reader.cpp // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: src/lib_json/json_batchallocator.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 JSONCPP_BATCHALLOCATOR_H_INCLUDED # define JSONCPP_BATCHALLOCATOR_H_INCLUDED # include # include # ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION namespace Json { /* Fast memory allocator. * * This memory allocator allocates memory for a batch of object (specified by * the page size, the number of object in each page). * * It does not allow the destruction of a single object. All the allocated objects * can be destroyed at once. The memory can be either released or reused for future * allocation. * * The in-place new operator must be used to construct the object using the pointer * returned by allocate. */ template class BatchAllocator { public: typedef AllocatedType Type; BatchAllocator( unsigned int objectsPerPage = 255 ) : freeHead_( 0 ) , objectsPerPage_( objectsPerPage ) { // printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space. assert( objectsPerPage >= 16 ); batches_ = allocateBatch( 0 ); // allocated a dummy page currentBatch_ = batches_; } ~BatchAllocator() { for ( BatchInfo *batch = batches_; batch; ) { BatchInfo *nextBatch = batch->next_; free( batch ); batch = nextBatch; } } /// allocate space for an array of objectPerAllocation object. /// @warning it is the responsability of the caller to call objects constructors. AllocatedType *allocate() { if ( freeHead_ ) // returns node from free list. { AllocatedType *object = freeHead_; freeHead_ = *(AllocatedType **)object; return object; } if ( currentBatch_->used_ == currentBatch_->end_ ) { currentBatch_ = currentBatch_->next_; while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ ) currentBatch_ = currentBatch_->next_; if ( !currentBatch_ ) // no free batch found, allocate a new one { currentBatch_ = allocateBatch( objectsPerPage_ ); currentBatch_->next_ = batches_; // insert at the head of the list batches_ = currentBatch_; } } AllocatedType *allocated = currentBatch_->used_; currentBatch_->used_ += objectPerAllocation; return allocated; } /// Release the object. /// @warning it is the responsability of the caller to actually destruct the object. void release( AllocatedType *object ) { assert( object != 0 ); *(AllocatedType **)object = freeHead_; freeHead_ = object; } private: struct BatchInfo { BatchInfo *next_; AllocatedType *used_; AllocatedType *end_; AllocatedType buffer_[objectPerAllocation]; }; // disabled copy constructor and assignement operator. BatchAllocator( const BatchAllocator & ); void operator =( const BatchAllocator &); static BatchInfo *allocateBatch( unsigned int objectsPerPage ) { const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; BatchInfo *batch = static_cast( malloc( mallocSize ) ); batch->next_ = 0; batch->used_ = batch->buffer_; batch->end_ = batch->buffer_ + objectsPerPage; return batch; } BatchInfo *batches_; BatchInfo *currentBatch_; /// Head of a single linked list within the allocated space of freeed object AllocatedType *freeHead_; unsigned int objectsPerPage_; }; } // namespace Json # endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION #endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: src/lib_json/json_batchallocator.h // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // 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() #ifndef JSON_VALUE_USE_INTERNAL_MAP : current_() , isNull_( true ) { } #else : isArray_( true ) , isNull_( true ) { iterator_.array_ = ValueInternalArray::IteratorState(); } #endif #ifndef JSON_VALUE_USE_INTERNAL_MAP ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator ¤t ) : current_( current ) , isNull_( false ) { } #else ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state ) : isArray_( true ) { iterator_.array_ = state; } ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state ) : isArray_( false ) { iterator_.map_ = state; } #endif Value & ValueIteratorBase::deref() const { #ifndef JSON_VALUE_USE_INTERNAL_MAP return current_->second; #else if ( isArray_ ) return ValueInternalArray::dereference( iterator_.array_ ); return ValueInternalMap::value( iterator_.map_ ); #endif } void ValueIteratorBase::increment() { #ifndef JSON_VALUE_USE_INTERNAL_MAP ++current_; #else if ( isArray_ ) ValueInternalArray::increment( iterator_.array_ ); ValueInternalMap::increment( iterator_.map_ ); #endif } void ValueIteratorBase::decrement() { #ifndef JSON_VALUE_USE_INTERNAL_MAP --current_; #else if ( isArray_ ) ValueInternalArray::decrement( iterator_.array_ ); ValueInternalMap::decrement( iterator_.map_ ); #endif } ValueIteratorBase::difference_type ValueIteratorBase::computeDistance( const SelfType &other ) const { #ifndef JSON_VALUE_USE_INTERNAL_MAP # ifdef JSON_USE_CPPTL_SMALLMAP return current_ - other.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 #else if ( isArray_ ) return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ ); return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ ); #endif } bool ValueIteratorBase::isEqual( const SelfType &other ) const { #ifndef JSON_VALUE_USE_INTERNAL_MAP if ( isNull_ ) { return other.isNull_; } return current_ == other.current_; #else if ( isArray_ ) return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ ); return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ ); #endif } void ValueIteratorBase::copy( const SelfType &other ) { #ifndef JSON_VALUE_USE_INTERNAL_MAP current_ = other.current_; #else if ( isArray_ ) iterator_.array_ = other.iterator_.array_; iterator_.map_ = other.iterator_.map_; #endif } Value ValueIteratorBase::key() const { #ifndef JSON_VALUE_USE_INTERNAL_MAP const Value::CZString czstring = (*current_).first; if ( czstring.c_str() ) { if ( czstring.isStaticString() ) return Value( StaticString( czstring.c_str() ) ); return Value( czstring.c_str() ); } return Value( czstring.index() ); #else if ( isArray_ ) return Value( ValueInternalArray::indexOf( iterator_.array_ ) ); bool isStatic; const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic ); if ( isStatic ) return Value( StaticString( memberName ) ); return Value( memberName ); #endif } UInt ValueIteratorBase::index() const { #ifndef JSON_VALUE_USE_INTERNAL_MAP const Value::CZString czstring = (*current_).first; if ( !czstring.c_str() ) return czstring.index(); return Value::UInt( -1 ); #else if ( isArray_ ) return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) ); return Value::UInt( -1 ); #endif } const char * ValueIteratorBase::memberName() const { #ifndef JSON_VALUE_USE_INTERNAL_MAP const char *name = (*current_).first.c_str(); return name ? name : ""; #else if ( !isArray_ ) return ValueInternalMap::key( iterator_.map_ ); return ""; #endif } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class ValueConstIterator // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// ValueConstIterator::ValueConstIterator() { } #ifndef JSON_VALUE_USE_INTERNAL_MAP ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator ¤t ) : ValueIteratorBase( current ) { } #else ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state ) : ValueIteratorBase( state ) { } ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state ) : ValueIteratorBase( state ) { } #endif ValueConstIterator & ValueConstIterator::operator =( const ValueIteratorBase &other ) { copy( other ); return *this; } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class ValueIterator // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// ValueIterator::ValueIterator() { } #ifndef JSON_VALUE_USE_INTERNAL_MAP ValueIterator::ValueIterator( const Value::ObjectValues::iterator ¤t ) : ValueIteratorBase( current ) { } #else ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state ) : ValueIteratorBase( state ) { } ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state ) : ValueIteratorBase( state ) { } #endif 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 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 #if !defined(JSON_IS_AMALGAMATION) # include # include # ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR # include "json_batchallocator.h" # endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR #endif // if !defined(JSON_IS_AMALGAMATION) #include #include #include #include #include #ifdef JSON_USE_CPPTL # include #endif #include // size_t #define JSON_ASSERT_UNREACHABLE assert( false ) #define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw #define JSON_FAIL_MESSAGE( message ) throw std::runtime_error( message ); #define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) JSON_FAIL_MESSAGE( message ) namespace Json { const Value Value::null; const Int Value::minInt = Int( ~(UInt(-1)/2) ); const Int Value::maxInt = Int( UInt(-1)/2 ); const UInt Value::maxUInt = UInt(-1); const Int64 Value::minInt64 = Int64( ~(UInt64(-1)/2) ); const Int64 Value::maxInt64 = Int64( UInt64(-1)/2 ); const UInt64 Value::maxUInt64 = UInt64(-1); const LargestInt Value::minLargestInt = LargestInt( ~(LargestUInt(-1)/2) ); const LargestInt Value::maxLargestInt = LargestInt( LargestUInt(-1)/2 ); const LargestUInt Value::maxLargestUInt = LargestUInt(-1); /// Unknown size marker static const unsigned int unknown = (unsigned)-1; /** 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, unsigned int length = unknown ) { if ( length == unknown ) length = (unsigned int)strlen(value); char *newString = static_cast( malloc( length + 1 ) ); JSON_ASSERT_MESSAGE( newString != 0, "Failed to allocate string value buffer" ); memcpy( newString, value, length ); newString[length] = 0; return newString; } /** Free the string duplicated by duplicateStringValue(). */ static inline void releaseStringValue( char *value ) { if ( value ) free( value ); } } // namespace Json // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ValueInternals... // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// #if !defined(JSON_IS_AMALGAMATION) # ifdef JSON_VALUE_USE_INTERNAL_MAP # include "json_internalarray.inl" # include "json_internalmap.inl" # endif // JSON_VALUE_USE_INTERNAL_MAP # include "json_valueiterator.inl" #endif // if !defined(JSON_IS_AMALGAMATION) namespace Json { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class Value::CommentInfo // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// Value::CommentInfo::CommentInfo() : comment_( 0 ) { } Value::CommentInfo::~CommentInfo() { if ( comment_ ) releaseStringValue( comment_ ); } void Value::CommentInfo::setComment( const char *text ) { if ( comment_ ) releaseStringValue( comment_ ); JSON_ASSERT( text != 0 ); JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /"); // It seems that /**/ style comments are acceptable as well. comment_ = duplicateStringValue( text ); } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class Value::CZString // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// # ifndef JSON_VALUE_USE_INTERNAL_MAP // Notes: index_ indicates if the string was allocated when // a string is stored. Value::CZString::CZString( ArrayIndex index ) : cstr_( 0 ) , index_( index ) { } Value::CZString::CZString( const char *cstr, DuplicationPolicy allocate ) : cstr_( allocate == duplicate ? duplicateStringValue(cstr) : cstr ) , index_( allocate ) { } Value::CZString::CZString( const CZString &other ) : cstr_( other.index_ != noDuplication && other.cstr_ != 0 ? duplicateStringValue( other.cstr_ ) : other.cstr_ ) , index_( other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) : other.index_ ) { } Value::CZString::~CZString() { if ( cstr_ && index_ == 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 =( const CZString &other ) { CZString temp( other ); swap( temp ); return *this; } bool Value::CZString::operator<( const CZString &other ) const { if ( cstr_ ) return strcmp( cstr_, other.cstr_ ) < 0; return index_ < other.index_; } bool Value::CZString::operator==( const CZString &other ) const { if ( cstr_ ) return strcmp( cstr_, other.cstr_ ) == 0; return index_ == other.index_; } ArrayIndex Value::CZString::index() const { return index_; } const char * Value::CZString::c_str() const { return cstr_; } bool Value::CZString::isStaticString() const { return index_ == noDuplication; } #endif // ifndef JSON_VALUE_USE_INTERNAL_MAP // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // 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 ) : type_( type ) , allocated_( 0 ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { 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; #ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: value_.map_ = new ObjectValues(); break; #else case arrayValue: value_.array_ = arrayAllocator()->newArray(); break; case objectValue: value_.map_ = mapAllocator()->newMap(); break; #endif case booleanValue: value_.bool_ = false; break; default: JSON_ASSERT_UNREACHABLE; } } #if defined(JSON_HAS_INT64) Value::Value( UInt value ) : type_( uintValue ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.uint_ = value; } Value::Value( Int value ) : type_( intValue ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.int_ = value; } #endif // if defined(JSON_HAS_INT64) Value::Value( Int64 value ) : type_( intValue ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.int_ = value; } Value::Value( UInt64 value ) : type_( uintValue ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.uint_ = value; } Value::Value( double value ) : type_( realValue ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.real_ = value; } Value::Value( const char *value ) : type_( stringValue ) , allocated_( true ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.string_ = duplicateStringValue( value ); } Value::Value( const char *beginValue, const char *endValue ) : type_( stringValue ) , allocated_( true ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.string_ = duplicateStringValue( beginValue, (unsigned int)(endValue - beginValue) ); } Value::Value( const std::string &value ) : type_( stringValue ) , allocated_( true ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.string_ = duplicateStringValue( value.c_str(), (unsigned int)value.length() ); } Value::Value( const StaticString &value ) : type_( stringValue ) , allocated_( false ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.string_ = const_cast( value.c_str() ); } # ifdef JSON_USE_CPPTL Value::Value( const CppTL::ConstString &value ) : type_( stringValue ) , allocated_( true ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.string_ = duplicateStringValue( value, value.length() ); } # endif Value::Value( bool value ) : type_( booleanValue ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.bool_ = value; } Value::Value( const Value &other ) : type_( other.type_ ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { switch ( type_ ) { case nullValue: case intValue: case uintValue: case realValue: case booleanValue: value_ = other.value_; break; case stringValue: if ( other.value_.string_ ) { value_.string_ = duplicateStringValue( other.value_.string_ ); allocated_ = true; } else value_.string_ = 0; break; #ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: value_.map_ = new ObjectValues( *other.value_.map_ ); break; #else case arrayValue: value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ ); break; case objectValue: value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ ); break; #endif 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_ ); } } } Value::~Value() { switch ( type_ ) { case nullValue: case intValue: case uintValue: case realValue: case booleanValue: break; case stringValue: if ( allocated_ ) releaseStringValue( value_.string_ ); break; #ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: delete value_.map_; break; #else case arrayValue: arrayAllocator()->destructArray( value_.array_ ); break; case objectValue: mapAllocator()->destructMap( value_.map_ ); break; #endif default: JSON_ASSERT_UNREACHABLE; } if ( comments_ ) delete[] comments_; } Value & Value::operator=( const Value &other ) { Value temp( other ); swap( temp ); return *this; } void Value::swap( 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; } 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: return ( value_.string_ == 0 && other.value_.string_ ) || ( other.value_.string_ && value_.string_ && strcmp( value_.string_, other.value_.string_ ) < 0 ); #ifndef JSON_VALUE_USE_INTERNAL_MAP 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_); } #else case arrayValue: return value_.array_->compare( *(other.value_.array_) ) < 0; case objectValue: return value_.map_->compare( *(other.value_.map_) ) < 0; #endif 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: return ( value_.string_ == other.value_.string_ ) || ( other.value_.string_ && value_.string_ && strcmp( value_.string_, other.value_.string_ ) == 0 ); #ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: return value_.map_->size() == other.value_.map_->size() && (*value_.map_) == (*other.value_.map_); #else case arrayValue: return value_.array_->compare( *(other.value_.array_) ) == 0; case objectValue: return value_.map_->compare( *(other.value_.map_) ) == 0; #endif default: JSON_ASSERT_UNREACHABLE; } return false; // unreachable } bool Value::operator !=( const Value &other ) const { return !( *this == other ); } const char * Value::asCString() const { JSON_ASSERT( type_ == stringValue ); return value_.string_; } std::string Value::asString() const { switch ( type_ ) { case nullValue: return ""; case stringValue: return value_.string_ ? value_.string_ : ""; case booleanValue: return value_.bool_ ? "true" : "false"; case intValue: case uintValue: case realValue: case arrayValue: case objectValue: JSON_FAIL_MESSAGE( "Type is not convertible to string" ); default: JSON_ASSERT_UNREACHABLE; } return ""; // unreachable } # ifdef JSON_USE_CPPTL CppTL::ConstString Value::asConstString() const { return CppTL::ConstString( asString().c_str() ); } # endif Value::Int Value::asInt() const { switch ( type_ ) { case nullValue: return 0; case intValue: JSON_ASSERT_MESSAGE( value_.int_ >= minInt && value_.int_ <= maxInt, "unsigned integer out of signed int range" ); return Int(value_.int_); case uintValue: JSON_ASSERT_MESSAGE( value_.uint_ <= UInt(maxInt), "unsigned integer out of signed int range" ); return Int(value_.uint_); case realValue: JSON_ASSERT_MESSAGE( value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range" ); return Int( value_.real_ ); case booleanValue: return value_.bool_ ? 1 : 0; case stringValue: case arrayValue: case objectValue: JSON_FAIL_MESSAGE( "Type is not convertible to int" ); default: JSON_ASSERT_UNREACHABLE; } return 0; // unreachable; } Value::UInt Value::asUInt() const { switch ( type_ ) { case nullValue: return 0; case intValue: JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to unsigned integer" ); JSON_ASSERT_MESSAGE( value_.int_ <= maxUInt, "signed integer out of UInt range" ); return UInt(value_.int_); case uintValue: JSON_ASSERT_MESSAGE( value_.uint_ <= maxUInt, "unsigned integer out of UInt range" ); return UInt(value_.uint_); case realValue: JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range" ); return UInt( value_.real_ ); case booleanValue: return value_.bool_ ? 1 : 0; case stringValue: case arrayValue: case objectValue: JSON_FAIL_MESSAGE( "Type is not convertible to uint" ); default: JSON_ASSERT_UNREACHABLE; } return 0; // unreachable; } # if defined(JSON_HAS_INT64) Value::Int64 Value::asInt64() const { switch ( type_ ) { case nullValue: return 0; case intValue: return value_.int_; case uintValue: JSON_ASSERT_MESSAGE( value_.uint_ <= UInt64(maxInt64), "unsigned integer out of Int64 range" ); return value_.uint_; case realValue: JSON_ASSERT_MESSAGE( value_.real_ >= minInt64 && value_.real_ <= maxInt64, "Real out of Int64 range" ); return Int( value_.real_ ); case booleanValue: return value_.bool_ ? 1 : 0; case stringValue: case arrayValue: case objectValue: JSON_FAIL_MESSAGE( "Type is not convertible to Int64" ); default: JSON_ASSERT_UNREACHABLE; } return 0; // unreachable; } Value::UInt64 Value::asUInt64() const { switch ( type_ ) { case nullValue: return 0; case intValue: JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to UInt64" ); return value_.int_; case uintValue: return value_.uint_; case realValue: JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt64, "Real out of UInt64 range" ); return UInt( value_.real_ ); case booleanValue: return value_.bool_ ? 1 : 0; case stringValue: case arrayValue: case objectValue: JSON_FAIL_MESSAGE( "Type is not convertible to UInt64" ); default: JSON_ASSERT_UNREACHABLE; } return 0; // unreachable; } # 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 nullValue: return 0.0; 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 static_cast( Int(value_.uint_/2) ) * 2 + Int(value_.uint_ & 1); #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) case realValue: return value_.real_; case booleanValue: return value_.bool_ ? 1.0 : 0.0; case stringValue: case arrayValue: case objectValue: JSON_FAIL_MESSAGE( "Type is not convertible to double" ); default: JSON_ASSERT_UNREACHABLE; } return 0; // unreachable; } float Value::asFloat() const { switch ( type_ ) { case nullValue: return 0.0f; 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 static_cast( Int(value_.uint_/2) ) * 2 + Int(value_.uint_ & 1); #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) case realValue: return static_cast( value_.real_ ); case booleanValue: return value_.bool_ ? 1.0f : 0.0f; case stringValue: case arrayValue: case objectValue: JSON_FAIL_MESSAGE( "Type is not convertible to float" ); default: JSON_ASSERT_UNREACHABLE; } return 0.0f; // unreachable; } bool Value::asBool() const { switch ( type_ ) { case nullValue: return false; case intValue: case uintValue: return value_.int_ != 0; case realValue: return value_.real_ != 0.0; case booleanValue: return value_.bool_; case stringValue: return value_.string_ && value_.string_[0] != 0; case arrayValue: case objectValue: return value_.map_->size() != 0; default: JSON_ASSERT_UNREACHABLE; } return false; // unreachable; } bool Value::isConvertibleTo( ValueType other ) const { switch ( type_ ) { case nullValue: return true; case intValue: return ( other == nullValue && value_.int_ == 0 ) || other == intValue || ( other == uintValue && value_.int_ >= 0 ) || other == realValue || other == stringValue || other == booleanValue; case uintValue: return ( other == nullValue && value_.uint_ == 0 ) || ( other == intValue && value_.uint_ <= (unsigned)maxInt ) || other == uintValue || other == realValue || other == stringValue || other == booleanValue; case realValue: return ( other == nullValue && value_.real_ == 0.0 ) || ( other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt ) || ( other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt ) || other == realValue || other == stringValue || other == booleanValue; case booleanValue: return ( other == nullValue && value_.bool_ == false ) || other == intValue || other == uintValue || other == realValue || other == stringValue || other == booleanValue; case stringValue: return other == stringValue || ( other == nullValue && (!value_.string_ || value_.string_[0] == 0) ); case arrayValue: return other == arrayValue || ( other == nullValue && value_.map_->size() == 0 ); case objectValue: return other == objectValue || ( other == nullValue && value_.map_->size() == 0 ); default: JSON_ASSERT_UNREACHABLE; } return false; // unreachable; } /// 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; #ifndef JSON_VALUE_USE_INTERNAL_MAP 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() ); #else case arrayValue: return Int( value_.array_->size() ); case objectValue: return Int( value_.map_->size() ); #endif default: 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( type_ == nullValue || type_ == arrayValue || type_ == objectValue ); switch ( type_ ) { #ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: value_.map_->clear(); break; #else case arrayValue: value_.array_->clear(); break; case objectValue: value_.map_->clear(); break; #endif default: break; } } void Value::resize( ArrayIndex newSize ) { JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); if ( type_ == nullValue ) *this = Value( arrayValue ); #ifndef JSON_VALUE_USE_INTERNAL_MAP 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 ); } #else value_.array_->resize( newSize ); #endif } Value & Value::operator[]( ArrayIndex index ) { JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); if ( type_ == nullValue ) *this = Value( arrayValue ); #ifndef JSON_VALUE_USE_INTERNAL_MAP 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, null ); it = value_.map_->insert( it, defaultValue ); return (*it).second; #else return value_.array_->resolveReference( index ); #endif } Value & Value::operator[]( int index ) { JSON_ASSERT( index >= 0 ); return (*this)[ ArrayIndex(index) ]; } const Value & Value::operator[]( ArrayIndex index ) const { JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); if ( type_ == nullValue ) return null; #ifndef JSON_VALUE_USE_INTERNAL_MAP CZString key( index ); ObjectValues::const_iterator it = value_.map_->find( key ); if ( it == value_.map_->end() ) return null; return (*it).second; #else Value *value = value_.array_->find( index ); return value ? *value : null; #endif } const Value & Value::operator[]( int index ) const { JSON_ASSERT( index >= 0 ); return (*this)[ ArrayIndex(index) ]; } Value & Value::operator[]( const char *key ) { return resolveReference( key, false ); } Value & Value::resolveReference( const char *key, bool isStatic ) { JSON_ASSERT( type_ == nullValue || type_ == objectValue ); if ( type_ == nullValue ) *this = Value( objectValue ); #ifndef JSON_VALUE_USE_INTERNAL_MAP CZString actualKey( key, isStatic ? CZString::noDuplication : 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, null ); it = value_.map_->insert( it, defaultValue ); Value &value = (*it).second; return value; #else return value_.map_->resolveReference( key, isStatic ); #endif } Value Value::get( ArrayIndex index, const Value &defaultValue ) const { const Value *value = &((*this)[index]); return value == &null ? defaultValue : *value; } bool Value::isValidIndex( ArrayIndex index ) const { return index < size(); } const Value & Value::operator[]( const char *key ) const { JSON_ASSERT( type_ == nullValue || type_ == objectValue ); if ( type_ == nullValue ) return null; #ifndef JSON_VALUE_USE_INTERNAL_MAP CZString actualKey( key, CZString::noDuplication ); ObjectValues::const_iterator it = value_.map_->find( actualKey ); if ( it == value_.map_->end() ) return null; return (*it).second; #else const Value *value = value_.map_->find( key ); return value ? *value : null; #endif } Value & Value::operator[]( const std::string &key ) { return (*this)[ key.c_str() ]; } const Value & Value::operator[]( const std::string &key ) const { return (*this)[ key.c_str() ]; } Value & Value::operator[]( const StaticString &key ) { return resolveReference( key, true ); } # ifdef JSON_USE_CPPTL Value & Value::operator[]( const CppTL::ConstString &key ) { return (*this)[ key.c_str() ]; } const Value & Value::operator[]( const CppTL::ConstString &key ) const { return (*this)[ key.c_str() ]; } # endif Value & Value::append( const Value &value ) { return (*this)[size()] = value; } Value Value::get( const char *key, const Value &defaultValue ) const { const Value *value = &((*this)[key]); return value == &null ? defaultValue : *value; } Value Value::get( const std::string &key, const Value &defaultValue ) const { return get( key.c_str(), defaultValue ); } Value Value::removeMember( const char* key ) { JSON_ASSERT( type_ == nullValue || type_ == objectValue ); if ( type_ == nullValue ) return null; #ifndef JSON_VALUE_USE_INTERNAL_MAP CZString actualKey( key, CZString::noDuplication ); ObjectValues::iterator it = value_.map_->find( actualKey ); if ( it == value_.map_->end() ) return null; Value old(it->second); value_.map_->erase(it); return old; #else Value *value = value_.map_->find( key ); if (value){ Value old(*value); value_.map_.remove( key ); return old; } else { return null; } #endif } Value Value::removeMember( const std::string &key ) { return removeMember( key.c_str() ); } # ifdef JSON_USE_CPPTL Value Value::get( const CppTL::ConstString &key, const Value &defaultValue ) const { return get( key.c_str(), defaultValue ); } # endif bool Value::isMember( const char *key ) const { const Value *value = &((*this)[key]); return value != &null; } bool Value::isMember( const std::string &key ) const { return isMember( key.c_str() ); } # ifdef JSON_USE_CPPTL bool Value::isMember( const CppTL::ConstString &key ) const { return isMember( key.c_str() ); } #endif Value::Members Value::getMemberNames() const { JSON_ASSERT( type_ == nullValue || type_ == objectValue ); if ( type_ == nullValue ) return Value::Members(); Members members; members.reserve( value_.map_->size() ); #ifndef JSON_VALUE_USE_INTERNAL_MAP 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.c_str() ) ); #else ValueInternalMap::IteratorState it; ValueInternalMap::IteratorState itEnd; value_.map_->makeBeginIterator( it ); value_.map_->makeEndIterator( itEnd ); for ( ; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) ) members.push_back( std::string( ValueInternalMap::key( it ) ) ); #endif 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 bool Value::isNull() const { return type_ == nullValue; } bool Value::isBool() const { return type_ == booleanValue; } bool Value::isInt() const { return type_ == intValue; } bool Value::isUInt() const { return type_ == uintValue; } bool Value::isIntegral() const { return type_ == intValue || type_ == uintValue || type_ == booleanValue; } bool Value::isDouble() const { return type_ == realValue; } bool Value::isNumeric() const { return isIntegral() || isDouble(); } bool Value::isString() const { return type_ == stringValue; } bool Value::isArray() const { return type_ == nullValue || type_ == arrayValue; } bool Value::isObject() const { return type_ == nullValue || type_ == objectValue; } void Value::setComment( const char *comment, CommentPlacement placement ) { if ( !comments_ ) comments_ = new CommentInfo[numberOfCommentPlacement]; comments_[placement].setComment( comment ); } void Value::setComment( const std::string &comment, CommentPlacement placement ) { setComment( comment.c_str(), 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_ ) { #ifdef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: if ( value_.array_ ) { ValueInternalArray::IteratorState it; value_.array_->makeBeginIterator( it ); return const_iterator( it ); } break; case objectValue: if ( value_.map_ ) { ValueInternalMap::IteratorState it; value_.map_->makeBeginIterator( it ); return const_iterator( it ); } break; #else case arrayValue: case objectValue: if ( value_.map_ ) return const_iterator( value_.map_->begin() ); break; #endif default: break; } return const_iterator(); } Value::const_iterator Value::end() const { switch ( type_ ) { #ifdef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: if ( value_.array_ ) { ValueInternalArray::IteratorState it; value_.array_->makeEndIterator( it ); return const_iterator( it ); } break; case objectValue: if ( value_.map_ ) { ValueInternalMap::IteratorState it; value_.map_->makeEndIterator( it ); return const_iterator( it ); } break; #else case arrayValue: case objectValue: if ( value_.map_ ) return const_iterator( value_.map_->end() ); break; #endif default: break; } return const_iterator(); } Value::iterator Value::begin() { switch ( type_ ) { #ifdef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: if ( value_.array_ ) { ValueInternalArray::IteratorState it; value_.array_->makeBeginIterator( it ); return iterator( it ); } break; case objectValue: if ( value_.map_ ) { ValueInternalMap::IteratorState it; value_.map_->makeBeginIterator( it ); return iterator( it ); } break; #else case arrayValue: case objectValue: if ( value_.map_ ) return iterator( value_.map_->begin() ); break; #endif default: break; } return iterator(); } Value::iterator Value::end() { switch ( type_ ) { #ifdef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: if ( value_.array_ ) { ValueInternalArray::IteratorState it; value_.array_->makeEndIterator( it ); return iterator( it ); } break; case objectValue: if ( value_.map_ ) { ValueInternalMap::IteratorState it; value_.map_->makeEndIterator( it ); return iterator( it ); } break; #else case arrayValue: case objectValue: if ( value_.map_ ) return iterator( value_.map_->end() ); break; #endif default: break; } return iterator(); } // class PathArgument // ////////////////////////////////////////////////////////////////// PathArgument::PathArgument() : kind_( kindNone ) { } PathArgument::PathArgument( ArrayIndex index ) : index_( index ) , kind_( kindIndex ) { } PathArgument::PathArgument( const char *key ) : key_( key ) , kind_( kindKey ) { } PathArgument::PathArgument( const std::string &key ) : key_( key.c_str() ) , 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::null ) { // 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::null ) 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 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 #if !defined(JSON_IS_AMALGAMATION) # include # include "json_tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include #include #include #include #include #include #if _MSC_VER >= 1400 // VC++ 8.0 #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. #endif namespace Json { static bool containsControlCharacter( const char* str ) { while ( *str ) { if ( isControlCharacter( *(str++) ) ) return true; } 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 ) { char buffer[32]; #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. sprintf_s(buffer, sizeof(buffer), "%#.16g", value); #else sprintf(buffer, "%#.16g", value); #endif char* ch = buffer + strlen(buffer) - 1; if (*ch != '0') return buffer; // nothing to truncate, so save time while(ch > buffer && *ch == '0'){ --ch; } char* last_nonzero = ch; while(ch >= buffer){ switch(*ch){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': --ch; continue; case '.': // Truncate zeroes to save bytes in output, but keep one. *(last_nonzero+2) = '\0'; return buffer; default: return buffer; } } return buffer; } std::string valueToString( bool value ) { return value ? "true" : "false"; } std::string valueToQuotedString( const char *value ) { // 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; } // 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: document_ += valueToQuotedString( value.asCString() ); 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_ += valueToQuotedString( name.c_str() ); document_ += yamlCompatiblityEnabled_ ? ": " : ":"; writeValue( value[name] ); } document_ += "}"; } break; } } // Class StyledWriter // ////////////////////////////////////////////////////////////////// StyledWriter::StyledWriter() : rightMargin_( 74 ) , indentSize_( 3 ) { } 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: pushValue( valueToQuotedString( value.asCString() ) ); 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 && !isMultiLine; ++index ) { writeValue( value[index] ); lineLength += int( childValues_[index].length() ); isMultiLine = isMultiLine && hasCommentForValue( value[index] ); } 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_ += normalizeEOL( root.getComment( commentBefore ) ); document_ += "\n"; } void StyledWriter::writeCommentAfterValueOnSameLine( const Value &root ) { if ( root.hasComment( commentAfterOnSameLine ) ) document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); if ( root.hasComment( commentAfter ) ) { document_ += "\n"; document_ += normalizeEOL( root.getComment( commentAfter ) ); document_ += "\n"; } } bool StyledWriter::hasCommentForValue( const Value &value ) { return value.hasComment( commentBefore ) || value.hasComment( commentAfterOnSameLine ) || value.hasComment( commentAfter ); } std::string StyledWriter::normalizeEOL( const std::string &text ) { std::string normalized; normalized.reserve( text.length() ); const char *begin = text.c_str(); const char *end = begin + text.length(); const char *current = begin; while ( current != end ) { char c = *current++; if ( c == '\r' ) // mac or dos EOL { if ( *current == '\n' ) // convert dos EOL ++current; normalized += '\n'; } else // handle unix EOL & other char normalized += c; } return normalized; } // Class StyledStreamWriter // ////////////////////////////////////////////////////////////////// StyledStreamWriter::StyledStreamWriter( std::string indentation ) : document_(NULL) , rightMargin_( 74 ) , indentation_( indentation ) { } void StyledStreamWriter::write( std::ostream &out, const Value &root ) { document_ = &out; addChildValues_ = false; indentString_ = ""; writeCommentBeforeValue( root ); 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: pushValue( valueToQuotedString( value.asCString() ) ); 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 { 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 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 && !isMultiLine; ++index ) { writeValue( value[index] ); lineLength += int( childValues_[index].length() ); isMultiLine = isMultiLine && hasCommentForValue( value[index] ); } 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() { /* Some comments in this method would have been nice. ;-) 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_ << '\n' << indentString_; } void StyledStreamWriter::writeWithIndent( const std::string &value ) { writeIndent(); *document_ << value; } 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; *document_ << normalizeEOL( root.getComment( commentBefore ) ); *document_ << "\n"; } void StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root ) { if ( root.hasComment( commentAfterOnSameLine ) ) *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); if ( root.hasComment( commentAfter ) ) { *document_ << "\n"; *document_ << normalizeEOL( root.getComment( commentAfter ) ); *document_ << "\n"; } } bool StyledStreamWriter::hasCommentForValue( const Value &value ) { return value.hasComment( commentBefore ) || value.hasComment( commentAfterOnSameLine ) || value.hasComment( commentAfter ); } std::string StyledStreamWriter::normalizeEOL( const std::string &text ) { std::string normalized; normalized.reserve( text.length() ); const char *begin = text.c_str(); const char *end = begin + text.length(); const char *current = begin; while ( current != end ) { char c = *current++; if ( c == '\r' ) // mac or dos EOL { if ( *current == '\n' ) // convert dos EOL ++current; normalized += '\n'; } else // handle unix EOL & other char normalized += c; } return normalized; } std::ostream& operator<<( std::ostream &sout, const Value &root ) { Json::StyledStreamWriter writer; writer.write(sout, root); return sout; } } // namespace Json // ////////////////////////////////////////////////////////////////////// // End of content of file: src/lib_json/json_writer.cpp // ////////////////////////////////////////////////////////////////////// sysdig-0.1.87/userspace/libsinsp/third-party/tinydir.h000066400000000000000000000206671237051215500230440ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/threadinfo.cpp000066400000000000000000000571451237051215500215720ustar00rootroot00000000000000/* 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" 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; m_progid = -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_main_thread = NULL; m_main_program_thread = NULL; m_lastevent_fd = 0; #ifdef HAS_FILTERING m_last_latency_entertime = 0; m_latency = 0; #endif m_ainfo = 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); it->second.set_role_server(); } else { it->second.set_role_client(); } } } } void sinsp_threadinfo::init(const scap_threadinfo* pi) { scap_fdinfo *fdi; scap_fdinfo *tfdi; sinsp_fdinfo_t newfdi; string tcomm(pi->comm); init(); m_tid = pi->tid; m_pid = pi->pid; m_ptid = pi->ptid; m_comm = pi->comm; if(tcomm == "" || tcomm[tcomm.length() - 1] == '/') { string ts(pi->exe); size_t commbegin = ts.rfind('/'); if(commbegin != string::npos) { m_comm = ts.substr(commbegin + 1); } } m_exe = pi->exe; set_args(pi->args, pi->args_len); set_cwd(pi->cwd, (uint32_t)strlen(pi->cwd)); m_flags |= pi->flags; 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; HASH_ITER(hh, pi->fdlist, fdi, tfdi) { 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; newfdi.m_write_callbacks.clear(); newfdi.m_read_callbacks.clear(); 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); 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); // // 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); } 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); } 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); // // 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); } } } 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; } } bool sinsp_threadinfo::is_main_thread() { return m_tid == m_pid; } 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 = m_inspector->get_thread(m_pid, true, true); if(NULL == ptinfo) { ASSERT(false); return NULL; } m_main_thread = ptinfo; } } return m_main_thread; } sinsp_threadinfo* sinsp_threadinfo::get_parent_thread() { return m_inspector->get_thread(m_ptid, false, true); } sinsp_fdtable* sinsp_threadinfo::get_fd_table() { sinsp_threadinfo* root; if(!(m_flags & PPM_CL_CLONE_FILES)) { root = this; } else { root = get_main_thread(); if(NULL == root) { ASSERT(false); return NULL; } } return &(root->m_fdtable); } 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); } sinsp_fdinfo_t* sinsp_threadinfo::get_fd(int64_t fd) { if(fd < 0) { return NULL; } sinsp_fdtable* fdt = get_fd_table(); if(fdt) { return fdt->find(fd); } else { ASSERT(false); } return NULL; } 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; } void sinsp_threadinfo::store_event(sinsp_evt *evt) { 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 // memcpy(m_lastevent_data, evt->m_pevt, elen); m_lastevent_cpuid = evt->get_cpuid(); } bool sinsp_threadinfo::is_lastevent_data_valid() { return (m_lastevent_cpuid != (uint16_t) - 1); } void sinsp_threadinfo::set_lastevent_data_validity(bool isvalid) { if(isvalid) { m_lastevent_cpuid = (uint16_t)1; } else { 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; } } 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_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; } sinsp_threadinfo* sinsp_thread_manager::get_thread(int64_t tid, bool lookup_only) { threadinfo_map_iterator_t it; // // Try looking up in our simple cache // if(m_last_tinfo && tid == m_last_tid) { #ifdef GATHER_INTERNAL_STATS m_cached_lookups->increment(); #endif m_last_tinfo->m_lastaccess_ts = m_inspector->m_lastevent_ts; return m_last_tinfo; } // // Caching failed, do a real lookup // it = m_threadtable.find(tid); if(it != m_threadtable.end()) { #ifdef GATHER_INTERNAL_STATS m_non_cached_lookups->increment(); #endif if(!lookup_only) { m_last_tid = tid; m_last_tinfo = &(it->second); m_last_tinfo->m_lastaccess_ts = m_inspector->m_lastevent_ts; } return &(it->second); } else { #ifdef GATHER_INTERNAL_STATS m_failed_lookups->increment(); #endif return NULL; } } 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::increment_program_childcount(sinsp_threadinfo* threadinfo, uint32_t level, uint32_t notclosed_level) { if(threadinfo->is_main_thread()) { sinsp_threadinfo* parent_thread = m_inspector->get_thread(threadinfo->m_ptid, false, false); if(parent_thread) { if(parent_thread->m_tid == threadinfo->m_tid || level > 64) { return; } if((parent_thread->m_comm == threadinfo->m_comm) && (parent_thread->m_exe == threadinfo->m_exe)) { threadinfo->m_progid = parent_thread->m_tid; ++parent_thread->m_nchilds; if(!(threadinfo->m_flags & PPM_CL_CLOSED)) { notclosed_level++; } increment_program_childcount(parent_thread, level + 1, notclosed_level); } } } } // Don't set level, it's for internal use void sinsp_thread_manager::decrement_program_childcount(sinsp_threadinfo* threadinfo, uint32_t level) { if(threadinfo->is_main_thread()) { ASSERT(threadinfo->m_pid != threadinfo->m_progid); sinsp_threadinfo* prog_thread = m_inspector->get_thread(threadinfo->m_progid, false, true); if(prog_thread) { if(prog_thread->m_nchilds > 0) { --prog_thread->m_nchilds; decrement_program_childcount(prog_thread, level + 1); threadinfo->m_main_program_thread = NULL; } else { ASSERT(false); } } if(level == 0) { threadinfo->m_progid = -1LL; threadinfo->m_main_program_thread = NULL; } } } void sinsp_thread_manager::add_thread(sinsp_threadinfo& threadinfo, bool from_scap_proctable) { #ifdef GATHER_INTERNAL_STATS m_added_threads->increment(); #endif if(m_threadtable.size() >= m_inspector->m_max_thread_table_size) { m_n_drops++; return; } if(!from_scap_proctable) { increment_mainthread_childcount(&threadinfo); increment_program_childcount(&threadinfo, 0, 0); } 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); } } else if(it->second.m_progid != -1LL) { decrement_program_childcount(&it->second); } // // 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::remove_inactive_threads() { if(m_last_flush_time_ns == 0) { m_last_flush_time_ns = m_inspector->m_lastevent_ts; } if(m_inspector->m_lastevent_ts > m_last_flush_time_ns + m_inspector->m_inactive_thread_scan_time_ns) { m_last_flush_time_ns = m_inspector->m_lastevent_ts; g_logger.format(sinsp_logger::SEV_INFO, "Flusing 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)) { // // 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(); } } 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::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; it->second.m_main_program_thread = NULL; it->second.m_progid = -1LL; sinsp_fdtable* fdt = it->second.get_fd_table(); if(fdt != NULL) { fdt->m_last_accessed_fd = -1LL; } } } 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); increment_program_childcount(&it->second, 0, 0); } } 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.1.87/userspace/libsinsp/threadinfo.h000066400000000000000000000222431237051215500212260ustar00rootroot00000000000000/* 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 true if this is a process' main thread. */ bool is_main_thread(); /*! \brief Get the main thread of the process containing this thread. */ sinsp_threadinfo* get_main_thread(); /*! \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 */ sinsp_fdinfo_t* get_fd(int64_t fd); /*! \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(); /*! \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. int64_t m_progid; ///< Main program id. If this process is part of a logical group of processes (e.g. it's one of the apache processes), the tid of the process that is the head of this group. string m_comm; ///< Command name (e.g. "top") string m_exe; ///< Full command name (e.g. "/bin/top") vector m_args; ///< Command line arguments (e.g. "-d1") 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. // // 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(); void init(const scap_threadinfo* pi); void fix_sockets_coming_from_proc(); sinsp_fdinfo_t* add_fd(int64_t fd, sinsp_fdinfo_t *fdinfo); void remove_fd(int64_t fd); sinsp_fdtable* get_fd_table(); void set_cwd(const char *cwd, uint32_t cwdlen); sinsp_threadinfo* get_cwd_root(); void set_args(const char* args, size_t len); void store_event(sinsp_evt *evt); bool is_lastevent_data_valid(); void set_lastevent_data_validity(bool isvalid); void allocate_private_state(); // 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; sinsp_threadinfo* m_main_program_thread; uint8_t m_lastevent_data[SP_EVT_BUF_SIZE]; // 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; 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); // // 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 for // just for lookup reason. In that case, m_lastaccess_ts is not updated // and m_last_tinfo is not set. // sinsp_threadinfo* get_thread(int64_t tid, bool lookup_only); void add_thread(sinsp_threadinfo& threadinfo, bool from_scap_proctable=false); void remove_thread(int64_t tid, bool force); void remove_thread(threadinfo_map_iterator_t it, bool force); void 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 increment_mainthread_childcount(sinsp_threadinfo* threadinfo); void increment_program_childcount(sinsp_threadinfo* threadinfo, uint32_t level, uint32_t notclosed_level); // Don't set level, it's for internal use void decrement_program_childcount(sinsp_threadinfo* threadinfo, uint32_t level = 0); 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.1.87/userspace/libsinsp/tuples.h000066400000000000000000000043111237051215500204130ustar00rootroot00000000000000/* 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.1.87/userspace/libsinsp/utils.cpp000066400000000000000000000555021237051215500206020ustar00rootroot00000000000000/* 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 #include #include "sinsp.h" #include "sinsp_int.h" #include "sinsp_errno.h" #include "sinsp_signal.h" #include "filter.h" #include "filterchecks.h" #include "protodecoder.h" #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, "./"}, {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"; default: ASSERT(false); return ""; } } // // errno to string conversion. // Only the first 40 error codes are currently 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) { 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) { snprintf(targetbuf, targetbuf_size, "%u.%u.%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)sinfo->m_ipv4info.m_fields.m_sport, (unsigned int)(uint8_t)db[0], (unsigned int)(uint8_t)db[1], (unsigned int)(uint8_t)db[2], (unsigned int)(uint8_t)db[3], (unsigned int)sinfo->m_ipv4info.m_fields.m_dport); } 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)) { snprintf(targetbuf, targetbuf_size, "%u.%u.%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)sinfo->m_ipv4info.m_fields.m_sport, (unsigned int)dip[0], (unsigned int)dip[1], (unsigned int)dip[2], (unsigned int)dip[3], (unsigned int)sinfo->m_ipv4info.m_fields.m_dport); 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:%u->%s:%u", srcstr, (unsigned int)sinfo->m_ipv4info.m_fields.m_sport, dststr, (unsigned int)sinfo->m_ipv4info.m_fields.m_dport); 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 /////////////////////////////////////////////////////////////////////////////// // 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 ipv4tuple_to_string(ipv4tuple* tuple) { char buf[50]; sprintf(buf, "%d.%d.%d.%d:%d->%d.%d.%d.%d:%d", (tuple->m_fields.m_sip & 0xFF), ((tuple->m_fields.m_sip & 0xFF00) >> 8), ((tuple->m_fields.m_sip & 0xFF0000) >> 16), ((tuple->m_fields.m_sip & 0xFF000000) >> 24), tuple->m_fields.m_sport, (tuple->m_fields.m_dip & 0xFF), ((tuple->m_fields.m_dip & 0xFF00) >> 8), ((tuple->m_fields.m_dip & 0xFF0000) >> 16), ((tuple->m_fields.m_dip & 0xFF000000) >> 24), tuple->m_fields.m_dport); return string(buf); } string ipv6tuple_to_string(_ipv6tuple* tuple) { 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:%u->%s:%u", source_address, tuple->m_fields.m_sport, destination_address, tuple->m_fields.m_dport); return string(buf); } string ipv4serveraddr_to_string(ipv4serverinfo* addr) { char buf[50]; sprintf(buf, "%d.%d.%d.%d:%d", (addr->m_ip & 0xFF), ((addr->m_ip & 0xFF00) >> 8), ((addr->m_ip & 0xFF0000) >> 16), ((addr->m_ip & 0xFF000000) >> 24), addr->m_port); return string(buf); } string ipv6serveraddr_to_string(ipv6serverinfo* addr) { char address[100]; char buf[200]; if(NULL == inet_ntop(AF_INET6, addr->m_ip, address, 100)) { return string(); } snprintf(buf,200,"%s:%u", address, addr->m_port); 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; } sysdig-0.1.87/userspace/libsinsp/utils.h000066400000000000000000000122651237051215500202460ustar00rootroot00000000000000/* 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); // // 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); }; /////////////////////////////////////////////////////////////////////////////// // 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 /////////////////////////////////////////////////////////////////////////////// string ipv4tuple_to_string(ipv4tuple* tuple); string ipv6tuple_to_string(_ipv6tuple* tuple); string ipv4serveraddr_to_string(ipv4serverinfo* addr); string ipv6serveraddr_to_string(ipv6serverinfo* addr); /////////////////////////////////////////////////////////////////////////////// // String helpers /////////////////////////////////////////////////////////////////////////////// vector sinsp_split(const string &s, 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); }; sysdig-0.1.87/userspace/sysdig/000077500000000000000000000000001237051215500164065ustar00rootroot00000000000000sysdig-0.1.87/userspace/sysdig/CMakeLists.txt000066400000000000000000000030471237051215500211520ustar00rootroot00000000000000include_directories("${JSONCPP_INCLUDE}") 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) else() set(SOURCE_FILES fields_info.cpp sysdig.cpp win32/getopt.c) endif() add_executable(sysdig ${SOURCE_FILES}) target_link_libraries(sysdig sinsp) if(NOT WIN32) add_subdirectory(man) install(TARGETS sysdig DESTINATION bin) install(DIRECTORY chisels DESTINATION share/sysdig) file(COPY chisels DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") else() target_link_libraries(sysdig 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") endif() configure_file(config.h.in config.h) sysdig-0.1.87/userspace/sysdig/chisels/000077500000000000000000000000001237051215500200405ustar00rootroot00000000000000sysdig-0.1.87/userspace/sysdig/chisels/COPYING000066400000000000000000000432541237051215500211030ustar00rootroot00000000000000 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.1.87/userspace/sysdig/chisels/ansiterminal.lua000066400000000000000000000037671237051215500232460ustar00rootroot00000000000000--[[ 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.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 return ansiterminal sysdig-0.1.87/userspace/sysdig/chisels/around.lua000066400000000000000000000067501237051215500220430ustar00rootroot00000000000000--[[ 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 -caround 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 = "srting", 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 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 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.1.87/userspace/sysdig/chisels/bottlenecks.lua000066400000000000000000000047311237051215500230650ustar00rootroot00000000000000--[[ 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 -- Interval callback, emits the output 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.1.87/userspace/sysdig/chisels/common.lua000066400000000000000000000140401237051215500220320ustar00rootroot00000000000000--[[ 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) local ccs = " " s = s .. string.sub(ccs, 0, newlen - string.len(s)) return s 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) then return string.format("%.2fP", val / (1024 * 1024 * 1024)) elseif val > (1024 * 1024 * 1024) then return string.format("%.2fT", val / (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 local header = extend_string(viz_info.value_desc, 10) for i, fldname in ipairs(viz_info.key_desc) do header = header .. extend_string(fldname, 10) 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, 10), 10) else keystr = keystr .. singlekey end end if viz_info.value_units == "none" then print(extend_string(v, 10) .. keystr) elseif viz_info.value_units == "bytes" then print(extend_string(format_bytes(v), 10) .. keystr) elseif viz_info.value_units == "time" then print(extend_string(format_time_interval(v), 10) .. 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, 10) .. keystr) end end end end sysdig-0.1.87/userspace/sysdig/chisels/dkjson.lua000066400000000000000000000637411237051215500220460ustar00rootroot00000000000000 -- 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 similiar 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.1.87/userspace/sysdig/chisels/echo_fds.lua000066400000000000000000000043761237051215500223270ustar00rootroot00000000000000--[[ 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."; 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 val == "disable_colors" 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") -- 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=<") chisel.set_event_formatter("%evt.arg.data") return true end -- Event parsing callback function on_event() buf = evt.field(fbuf) isread = evt.field(fisread) res = evt.field(fres) name = evt.field(fname) if name == nil then name = "" end if res <= 0 then return true end if isread then infostr = string.format("%s------ Read %s from %s", terminal.red, format_bytes(res), name) else infostr = string.format("%s------ Write %s to %s", terminal.blue, format_bytes(res), name) end print(infostr) return true end function on_capture_end() print(terminal.reset) end sysdig-0.1.87/userspace/sysdig/chisels/fdbytes_by.lua000066400000000000000000000027441237051215500227040ustar00rootroot00000000000000--[[ 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 = "Gropus 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.1.87/userspace/sysdig/chisels/fdcount_by.lua000066400000000000000000000044431237051215500227040ustar00rootroot00000000000000--[[ 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 = "Gropus 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 -- Interval callback, emits the ourput 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.1.87/userspace/sysdig/chisels/fdtime_by.lua000066400000000000000000000026341237051215500225120ustar00rootroot00000000000000--[[ 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 = "Gropus 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 = "IO" -- 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", "time") "" .. TOP_NUMBER, return true end sysdig-0.1.87/userspace/sysdig/chisels/fileslower.lua000066400000000000000000000046001237051215500227160ustar00rootroot00000000000000--[[ 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 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 . --]] description = "Trace file I/O slower than a threshold, or all file I/O"; short_description = "Trace slow file I/O"; category = "Performance"; skip_dev = true -- skip /dev/... files args = { { name = "min_ms", description = "minimum millisecond threshold for showing file I/O", argtype = "int" }, } function on_set_arg(name, val) min_ms = tonumber(val) return true end function on_init() -- set these 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") -- filter for file I/O chisel.set_filter("evt.is_io=true and fd.type=file") print(string.format("%-23.23s %-12.12s %-8s %7s %s", "TIME", "PROCESS", "TYPE", "LAT(ms)", "FILE")) return true end function on_event() 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 print(string.format("%-23.23s %-12.12s %-8s %7d %s", evt.field(datetime), evt.field(pname), evt.field(etype), lat, fn)) end end return true end sysdig-0.1.87/userspace/sysdig/chisels/iobytes.lua000066400000000000000000000032021237051215500222160ustar00rootroot00000000000000--[[ 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.1.87/userspace/sysdig/chisels/iobytes_file.lua000066400000000000000000000031021237051215500232140ustar00rootroot00000000000000--[[ 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.1.87/userspace/sysdig/chisels/iobytes_net.lua000066400000000000000000000032031237051215500230650ustar00rootroot00000000000000--[[ 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.1.87/userspace/sysdig/chisels/list_login_shells.lua000066400000000000000000000046451237051215500242710ustar00rootroot00000000000000--[[ 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."; short_description = "List the login shell IDs"; category = "Security"; 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 = {} 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 function on_init() fsid = chisel.request_field("proc.loginshellid") fexe = chisel.request_field("evt.arg.exe") fargs = chisel.request_field("evt.arg.args") chisel.set_filter("evt.type=execve") return true end function on_event() sid = evt.field(fsid) exe = evt.field(fexe) args = evt.field(fargs) 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[sid] = 1 end return true end 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 print(k) end end sysdig-0.1.87/userspace/sysdig/chisels/lsof.lua000066400000000000000000000040171237051215500215100ustar00rootroot00000000000000--[[ 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 very similar to the one of lsof"; short_description = "List the open file descriptors."; category = "System State"; -- Argument list args = {} -- Imports and globals require "common" local dctable = {} 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 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() return false end function on_capture_end() if not capturing then return end local ttable = sysdig.get_thread_table() 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 pairs(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.1.87/userspace/sysdig/chisels/netlower.lua000066400000000000000000000040761237051215500224110ustar00rootroot00000000000000--[[ netlower.lua - trace the syscalls slower than a given threshold. USAGE: sysdig -c netlower min_ms eg, sysdig -c netlower 1000 # show network I/O slower than 1000 ms. 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" short_description = "Trace slow network I/0" category = "Performance" -- Chisel argument list args = { { name = "min_msec", description = "minimum millisecond threshold for showing network I/O", argtype = "int" }, } -- Argument notification callback function on_set_arg(name, val) min_ms = tonumber(val) 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") -- filter for network I/O chisel.set_filter("evt.is_io=true and (fd.type=ipv4 or fd.type=ipv6)") print(string.format("%-23.23s %-12.12s %-8s %-12s %s", "TIME", "PROCESS", "TYPE", "LATENCY(ms)", "SOCKET")) return true end -- Event callback function on_event() lat = evt.field(latency) / 1000000 fn = evt.field(fname) if evt.field(dir) == "<" and lat > min_ms then print(string.format("%-23.23s %-12.12s %-8s %-12d %s", evt.field(datetime), evt.field(pname), evt.field(etype), lat, fn)) end return true endsysdig-0.1.87/userspace/sysdig/chisels/proc_exec_time.lua000066400000000000000000000043501237051215500235320ustar00rootroot00000000000000--[[ 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"; 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") -- set the filter chisel.set_filter("evt.type=procexit") 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 end print(color .. format_time_interval(duration) .. ") " .. evt.field(fexe) .. " " .. evt.field(fargs)) end return true end function on_capture_end() print(terminal.reset) end sysdig-0.1.87/userspace/sysdig/chisels/scallslower.lua000066400000000000000000000035601237051215500231010ustar00rootroot00000000000000--[[ 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. 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"; short_description = "Trace slow syscalls"; category = "Performance"; -- Chisel argument list args = { { name = "min_msec", description = "minimum milliseconds before which a syscall should complete", argtype = "int" }, } -- Argument notification callback function on_set_arg(name, val) min_msec = tonumber(val) 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") print(string.format("%-23.23s %-23.23s %-20s %s", "TIME", "PROCESS", "LATENCY(msec)", "SYSCALL")) return true end -- Event callback function on_event() lat = evt.field(latency) / 1000000 if lat > min_msec then print(string.format("%-23.23s %-23.23s %-20s %s", evt.field(datetime), evt.field(pname), lat, evt.field(etype))) end endsysdig-0.1.87/userspace/sysdig/chisels/spy_ip.lua000066400000000000000000000051671237051215500220570ustar00rootroot00000000000000--[[ 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 endpoint. 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.1.87/userspace/sysdig/chisels/spy_logs.lua000066400000000000000000000114651237051215500224110ustar00rootroot00000000000000--[[ 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."; 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 = "srting", 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 }, } -- 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 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") -- 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 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 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 infostr = string.format("%s%s%s", color, infostr, msg) else infostr = string.format("%s%s", infostr, msg) 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 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.1.87/userspace/sysdig/chisels/spy_port.lua000066400000000000000000000053231237051215500224250ustar00rootroot00000000000000--[[ 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.1.87/userspace/sysdig/chisels/spy_syslog.lua000066400000000000000000000104321237051215500227560ustar00rootroot00000000000000--[[ 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 = "srting", 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 }, } -- 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 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") -- 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 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 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) -- 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 end infostr = string.format("%s%s.%s %s[%u] %s", color, fac, sev, pname, tid, msg) else infostr = string.format("%s.%s %s[%u] %s", fac, sev, pname, tid, msg) end print(infostr) if do_dump then local hi, low = evt.get_ts() table.insert(entrylist, {hi, low, tid}) end return true end 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.1.87/userspace/sysdig/chisels/spy_users.lua000066400000000000000000000075661237051215500226150ustar00rootroot00000000000000--[[ 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"; 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 } } require "common" MAX_ANCESTOR_NAVIGATION = 16 max_depth = -1 -- Argument notification callback function on_set_arg(name, val) if name == "max_depth" then max_depth = tonumber(val) return true end return false end -- Initialization callback function on_init() -- Request the fields that we need 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") fanames = {} fapids = {} -- 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 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 aname local icorr = 1 if ischdir then ppid = pid table.insert(fanames, 0, 0) table.insert(fapids, 0, 0) icorr = 0 end if user == nil then user = "" end if not process_tree[ppid] then -- No parent pid in the table yet. -- Add one and make sure that there's a shell among the ancestors process_tree[ppid] = {-1} for j = 1, MAX_ANCESTOR_NAVIGATION do aname = evt.field(fanames[j]) if aname == nil then if evt.field(fapids[j]) == nil then -- no shell in the ancestor list, hide this command break end elseif string.len(aname) >= 2 and aname:sub(-2) == "sh" then apid = evt.field(fapids[j]) if process_tree[apid] then process_tree[ppid] = {j - 1, apid} else process_tree[ppid] = {0, apid} end end end end if process_tree[ppid][1] == -1 then -- the parent process has already been detected as NOT having a shell ancestor return true end if not process_tree[pid] then process_tree[pid] = {1 + process_tree[ppid][1], process_tree[ppid][2]} end if ischdir then if max_depth ~= -1 then if process_tree[pid][1] - icorr > max_depth then return true end end print(extend_string("", 4 * (process_tree[pid][1] - icorr)) .. process_tree[pid][2] .. " " .. dtime .. " " .. user .. ") " .. "cd " .. evt.field(fdir)) else if max_depth ~= -1 then if process_tree[pid][1] - 1 > max_depth then return true end end print(extend_string("", 4 * (process_tree[pid][1] - 1)) .. process_tree[pid][2] .. " " .. dtime .. " " .. user .. ") " .. evt.field(fexe) .. " " .. evt.field(fargs)) end return true end sysdig-0.1.87/userspace/sysdig/chisels/stderr.lua000066400000000000000000000024151237051215500220500ustar00rootroot00000000000000--[[ 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."; short_description = "Print stderr 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=2 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.1.87/userspace/sysdig/chisels/stdin.lua000066400000000000000000000024141237051215500216650ustar00rootroot00000000000000--[[ 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.1.87/userspace/sysdig/chisels/stdout.lua000066400000000000000000000024171237051215500220710ustar00rootroot00000000000000--[[ 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.1.87/userspace/sysdig/chisels/table_generator.lua000066400000000000000000000101761237051215500237050ustar00rootroot00000000000000--[[ 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 to the screen a table." short_description = "FD bytes group by" category = "IO" 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 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 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 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 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 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.1.87/userspace/sysdig/chisels/topconns.lua000066400000000000000000000023031237051215500224040ustar00rootroot00000000000000--[[ 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."; short_description = "top network connections by total bytes"; category = "Net"; -- 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", "fd.l4proto,fd.name", "Proto,Connection", "evt.rawarg.res", "Bytes", "(fd.type=ipv4 or fd.type=ipv6) and evt.is_io=true", "" .. TOP_NUMBER, "bytes") return true end sysdig-0.1.87/userspace/sysdig/chisels/topfiles_bytes.lua000066400000000000000000000021571237051215500236030ustar00rootroot00000000000000--[[ 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." 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() chisel.exec("table_generator", "fd.name", "Filename", "evt.rawarg.res", "Bytes", "fd.type=file and evt.is_io=true", "" .. TOP_NUMBER, "bytes") return true end sysdig-0.1.87/userspace/sysdig/chisels/topfiles_errors.lua000066400000000000000000000021531237051215500237650ustar00rootroot00000000000000--[[ 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." 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() chisel.exec("table_generator", "fd.name", "Filename", "evt.count", "#Errors", "fd.type=file and evt.failed=true", "100", "none") return true end sysdig-0.1.87/userspace/sysdig/chisels/topfiles_time.lua000066400000000000000000000021451237051215500234100ustar00rootroot00000000000000--[[ 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." 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() chisel.exec("table_generator", "fd.name", "Filename", "evt.latency", "Time", "fd.type=file and evt.is_io=true", "" .. TOP_NUMBER, "time") return true end sysdig-0.1.87/userspace/sysdig/chisels/topports_server.lua000066400000000000000000000022651237051215500240300ustar00rootroot00000000000000--[[ 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."; 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() chisel.exec("table_generator", "fd.sport", "Server Port", "evt.rawarg.res", "Bytes", "(fd.type=ipv4 or fd.type=ipv6) and evt.is_io=true", "" .. TOP_NUMBER, "bytes") return true end sysdig-0.1.87/userspace/sysdig/chisels/topprocs_cpu.lua000066400000000000000000000063221237051215500232660ustar00rootroot00000000000000--[[ 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 to the screen a table." short_description = "Top processes by CPU usage" category = "CPU Usage" -- Chisel argument list args = {} require "common" terminal = require "ansiterminal" grtable = {} islive = false cpustates = {} vizinfo = { key_fld = "proc.name", key_desc = {"Process"}, value_fld = "thread.exectime", value_desc = "CPU%", value_units = "timepct", top_number = 10, output_format = "normal" } function on_init() -- Request the fields we need fkey = chisel.request_field(vizinfo.key_fld) fvalue = chisel.request_field(vizinfo.value_fld) fnext = chisel.request_field("evt.arg.next") fnextraw = chisel.request_field("evt.rawarg.next") chisel.set_filter("evt.type=switch") return true end 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 ncpus = sysdig.get_machine_info().num_cpus for j = 1, ncpus do cpustates[j] = {0, 0, 0, ""} end return true end function on_event() key = evt.field(fkey) value = evt.field(fvalue) cpuid = evt.get_cpuid() + 1 if key ~= nil and value ~= nil and value > 0 then thissec = value - cpustates[cpuid][3] if thissec < 0 then thissec = 0 end if grtable[key] == nil then grtable[key] = thissec else grtable[key] = grtable[key] + thissec end cpustates[cpuid][1], cpustates[cpuid][2] = evt.get_ts() end if evt.field(fnext) ~= "" .. evt.field(fnextraw) then cpustates[cpuid][4] = evt.field(fnext) else cpustates[cpuid][4] = nil end cpustates[cpuid][3] = 0 return true end function on_interval(ts_s, ts_ns, delta) if vizinfo.output_format ~= "json" then terminal.clearscreen() terminal.moveto(0, 0) end for cpuid = 1, ncpus do if cpustates[cpuid][1] ~= 0 then cpustates[cpuid][3] = 1000000000 - cpustates[cpuid][2] key = cpustates[cpuid][4] if key ~= nil and value ~= nil and value > 0 then if grtable[key] == nil then grtable[key] = cpustates[cpuid][3] else grtable[key] = grtable[key] + cpustates[cpuid][3] end end end end print_sorted_table(grtable, ts_s, 0, delta, vizinfo) -- Clear the table grtable = {} return true end 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.1.87/userspace/sysdig/chisels/topprocs_errors.lua000066400000000000000000000020731237051215500240120ustar00rootroot00000000000000--[[ 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." 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() chisel.exec("table_generator", "proc.name", "Process", "evt.count", "#Errors", "evt.failed=true", "100", "none") return true end sysdig-0.1.87/userspace/sysdig/chisels/topprocs_file.lua000066400000000000000000000022171237051215500234150ustar00rootroot00000000000000--[[ 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." 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() chisel.exec("table_generator", "proc.name", "Process", "evt.rawarg.res", "Bytes", "fd.type=file and evt.is_io=true", "" .. TOP_NUMBER, "bytes") return true end sysdig-0.1.87/userspace/sysdig/chisels/topprocs_net.lua000066400000000000000000000020231237051215500232570ustar00rootroot00000000000000--[[ 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 = "Sorthed list of the processes that use the most network bandwidth." short_description = "Top processes by network I/O" category = "Net" -- Chisel argument list args = {} -- Initialization callback function on_init() chisel.exec("table_generator", "proc.name", "Process", "evt.rawarg.res", "Bytes", "(fd.type=ipv4 or fd.type=ipv6) and evt.is_io=true", "100", "bytes") return true end sysdig-0.1.87/userspace/sysdig/chisels/topscalls.lua000066400000000000000000000023401237051215500225460ustar00rootroot00000000000000--[[ 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." 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() chisel.exec("table_generator", "evt.type", "System Call", "evt.count", "# Calls", "evt.type!=switch", "" .. TOP_NUMBER, "none") return true end sysdig-0.1.87/userspace/sysdig/chisels/topscalls_time.lua000066400000000000000000000023151237051215500235660ustar00rootroot00000000000000--[[ 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." 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() chisel.exec("table_generator", "evt.type", "System Call", "evt.latency", "Time", "", "" .. TOP_NUMBER, "time") return true end sysdig-0.1.87/userspace/sysdig/config.h.in000066400000000000000000000001641237051215500204320ustar00rootroot00000000000000#pragma once #define SYSDIG_VERSION "${SYSDIG_VERSION}" #define SYSDIG_INSTALLATION_DIR "${CMAKE_INSTALL_PREFIX}" sysdig-0.1.87/userspace/sysdig/fields_info.cpp000066400000000000000000000156161237051215500214040ustar00rootroot00000000000000/* 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 "sysdig.h" #include "chisel.h" #define DESCRIPTION_TEXT_START 20 #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]; printf("\n----------------------\n"); printf("Field Class: %s\n\n", fci->m_name.c_str()); for(k = 0; k < fci->m_nfiedls; k++) { const filtercheck_field_info* fld = &fci->m_fields[k]; printf("%s", fld->m_name); uint32_t namelen = (uint32_t)strlen(fld->m_name); ASSERT(namelen < DESCRIPTION_TEXT_START); 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; 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) { 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 - DESCRIPTION_TEXT_START) == 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)); 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(); ASSERT(namelen < (DESCRIPTION_TEXT_START)); 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.1.87/userspace/sysdig/man/000077500000000000000000000000001237051215500171615ustar00rootroot00000000000000sysdig-0.1.87/userspace/sysdig/man/CMakeLists.txt000066400000000000000000000005741237051215500217270ustar00rootroot00000000000000find_program(PANDOC pandoc) if (PANDOC) add_custom_target(man 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) else() install(FILES sysdig.8 DESTINATION share/man/man8) endif() sysdig-0.1.87/userspace/sysdig/man/build.sh000077500000000000000000000000711237051215500206150ustar00rootroot00000000000000pandoc -s -f markdown_github -t man sysdig.md -o sysdig.8sysdig-0.1.87/userspace/sysdig/man/sysdig.8000066400000000000000000000212361237051215500205600ustar00rootroot00000000000000.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 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]\ \ \ \ \ \ \ \f[] .PP where: .IP \[bu] 2 evt.num is the incremental event number .PD 0 .P .PD .IP \[bu] 2 evt.time is the event timestamp .PD 0 .P .PD .IP \[bu] 2 evt.cpu is the CPU number where the event was captured .PD 0 .P .PD .IP \[bu] 2 proc.name is the name of the process that generated the event .PD 0 .P .PD .IP \[bu] 2 thread.tid id the TID that generated the event, which corresponds to the PID for single thread processes .PD 0 .P .PD .IP \[bu] 2 evt.dir is the event direction, > for enter events and < for exit events .PD 0 .P .PD .IP \[bu] 2 evt.type is the name of the event, e.g. \[aq]open\[aq] or \[aq]read\[aq] .PD 0 .P .PD .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 \f[B]Trace Files\f[] .PP A trace file can be created using the \-w switch: .RS .PP $ sysdig \-w trace.scap .RE .PP The \-s switch can be used to specify how many bytes of each data buffer should be saved to disk. And filters can be .PD 0 .P .PD used to save only certain events to disk: .RS .PP $ sysdig \-s 2000 \-w trace.scap proc.name=cat .RE .PP Trace files can be read this using the \-r switch: .RS .PP $ sysdig \-r trace.scap .RE .PP \f[B]Filtering\f[] .PP sysdig filters are specified at the end of the command line. The simplest filter is a basic field\-value check: .RS .PP $ sysdig proc.name=cat .RE .PP The list of available fields can be obtained with \[aq]sysdig \-l\[aq]. .PD 0 .P .PD Filter expressions can use one of these comparison operators: \f[I]=\f[], \f[I]!=\f[], \f[I]<\f[], \f[I]<=\f[], \f[I]>\f[], \f[I]>=\f[] and \f[I]contains\f[]. e.g. .RS .PP $ sysdig fd.name contains /etc .RE .PP Multiple checks can be combined through brakets 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]\-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]\-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 .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]\-h\f[], \f[B]\-\-help\f[] .PD 0 .P .PD Print this page .PP \f[B]\-j\f[], \f[B]\-\-json\f[] .PD 0 .P .PD Emit output as json .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]\-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[] \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. See the examples section below for more info. .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 diplayed. 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, and \f[B]d\f[] for delta between event enter and exit. .PP \f[B]\-v\f[], \f[B]\-\-verbose\f[] .PD 0 .P .PD Verbose output. .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]\-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 Read events from a file and print them to screen .RS .PP $ sysdig \-r dumpfile.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. .SS SEE ALSO .PP \f[B]strace\f[](8), \f[B]tcpdump\f[](8), \f[B]lsof\f[](8) sysdig-0.1.87/userspace/sysdig/man/sysdig.md000066400000000000000000000163161237051215500210140ustar00rootroot00000000000000NAME ---- sysdig - the definitive system and process troubleshooting tool SYNOPSIS -------- **sysdig** [*option*]... [*filter*] DESCRIPTION ----------- 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: ``` ``` 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'. **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: _=_, _!=_, _<_, _<=_, _>_, _>=_ and _contains_. e.g. > $ sysdig fd.name contains /etc Multiple checks can be combined through brakets 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. **-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. **-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 **-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. **-h**, **--help** Print this page **-j**, **--json** Emit output as json **-i _chiselname_**, **--chisel-info=**_chiselname_ Get a longer description and the arguments associated with a chisel found in the -cl option list. **-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** _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. See the examples section below for more info. **-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 diplayed. Accepted values are **h** for human-readable string, **a** for absolute timestamp from epoch, **r** for relative time from the beginning of the capture, and **d** for delta between event enter and exit. **-v**, **--verbose** Verbose output. **-w** _writefile_, **--write**=_writefile_ Write the captured events to _writefile_. **-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 Read events from a file and print them to screen > $ sysdig -r dumpfile.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. SEE ALSO -------- **strace**(8), **tcpdump**(8), **lsof**(8) sysdig-0.1.87/userspace/sysdig/sysdig.cpp000066400000000000000000000740141237051215500204220ustar00rootroot00000000000000/* 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 "sysdig.h" #include "chisel.h" #ifdef _WIN32 #include "win32/getopt.h" #include #else #include #include #endif static bool g_terminate = false; #ifdef HAS_CHISELS vector g_chisels; #endif // Sysdig 0.1.85 had log-rotation options (-C,-G,-W), but they were problematic, // so I'm disabling them until they can be fixed #define DISABLE_CGW static void usage(); // // Helper functions // static void signal_callback(int signal) { g_terminate = true; } 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; } } // // 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" #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 #ifndef DISABLE_CGW " -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" #endif " -d, --displayflt Make the given filter a display one\n" " Setting this option causes the events to be filtered\n" " after being parsed by the state system. Events are\n" " normally filtered before being analyzed, which is more\n" " efficient, but can cause state (e.g. FD names) to be lost.\n" " -D, --debug Capture events about sysdig itself\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" #ifndef DISABLE_CGW " -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, each new file will overwrite the\n" " previous.\n" "\n" " If used in conjunction with the -C option, filenames will take\n" " the form of `file'.\n" #endif " -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\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" " -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" " 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 diplayed. 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, and d for delta between event enter and exit.\n" " -v, --verbose Verbose output.\n" " -w , --write=\n" " Write the captured events to .\n" #ifndef DISABLE_CGW " -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. In addition, it\n" " will name the files with enough leading 0s to support the maximum number\n" " of files, allowing them to sort correctly.\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" #endif " -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.time %%evt.cpu %%proc.name (%%thread.tid) %%evt.dir %%evt.type %%evt.args\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.args 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" "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 += " "; } } 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. // chisels_on_capture_end(); } // // Event processing loop // captureinfo do_inspect(sinsp* inspector, uint64_t cnt, bool quiet, bool print_progress, sinsp_filter* display_filter, vector* summary_table, sinsp_evt_formatter* formatter) { captureinfo retval; int32_t res; sinsp_evt* ev; uint64_t ts; uint64_t deltats = 0; uint64_t firstts = 0; string line; double last_printed_progress_pct = 0; // // Loop through the events // while(1) { if(retval.m_nevts == cnt || g_terminate) { // // End of capture, either because the user stopped it, or because // we reached the event count specified with -n. // handle_end_of_file(print_progress, formatter); break; } res = inspector->next(&ev); if(res == SCAP_TIMEOUT) { if(ev != NULL && ev->is_filtered_out()) { // // The event has been dropped by the filtering system. // Give the chisels a chance to run their timeout logic. // chisels_do_timeout(ev); } continue; } else if(res == SCAP_EOF) { handle_end_of_file(print_progress, formatter); break; } else if(res != SCAP_SUCCESS) { // // Event read error. // Notify the chisels that we're exiting, and then die with an error. // handle_end_of_file(print_progress, formatter); cerr << "res = " << res << endl; throw sinsp_exception(inspector->getlasterr().c_str()); } retval.m_nevts++; ts = ev->get_ts(); if(firstts == 0) { firstts = ts; } deltats = ts - firstts; 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(formatter->tostring(ev, &line)) { // // Output the line // if(display_filter) { if(!display_filter->run(ev)) { continue; } } cout << line; if( inspector->get_buffer_format() != sinsp_evt::PF_JSON) { cout << endl; } else { cout << flush; } } } } retval.m_time = deltats; 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; captureinfo cinfo; string output_format; uint32_t snaplen = 0; int long_index = 0; int32_t n_filterargs = 0; int cflag = 0; string cname; vector* summary_table = NULL; string timefmt = "%evt.time"; // These variables are for the cycle_writer engine int duration_seconds = 0; int rollover_mb = 0; int file_limit = 0; bool do_cycle = false; static struct option long_options[] = { {"print-ascii", no_argument, 0, 'A' }, #ifdef HAS_CHISELS {"chisel", required_argument, 0, 'c' }, {"list-chisels", no_argument, &cflag, 1 }, #endif {"compress", no_argument, 0, 'z' }, {"displayflt", no_argument, 0, 'd' }, {"debug", no_argument, 0, 'D'}, {"fatfile", no_argument, 0, 'F'}, #ifndef DISABLE_CGW {"seconds", required_argument, 0, 'G' }, #endif {"help", no_argument, 0, 'h' }, #ifdef HAS_CHISELS {"chisel-info", required_argument, 0, 'i' }, #endif #ifndef DISABLE_CGW {"file-size", required_argument, 0, 'C' }, #endif {"json", no_argument, 0, 'j' }, {"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' }, {"verbose", no_argument, 0, 'v' }, {"writefile", required_argument, 0, 'w' }, #ifndef DISABLE_CGW {"limit", required_argument, 0, 'W' }, #endif {"print-hex", no_argument, 0, 'x'}, {"print-hex-ascii", no_argument, 0, 'X'}, {0, 0, 0, 0} }; output_format = "*%evt.num